feat(ci): add deploy workflow and Makefile
- Add .gitea/workflows/deploy.yml for CD pipeline - Add Makefile for build automation - Update .gitignore to exclude docs/
This commit is contained in:
130
.gitea/workflows/deploy.yml
Normal file
130
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- closed
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Image tag to deploy'
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
environment:
|
||||||
|
description: 'Deployment environment'
|
||||||
|
required: true
|
||||||
|
default: 'production'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- production
|
||||||
|
- staging
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: code.littlelan.cn
|
||||||
|
IMAGE_NAME: carrot_bbs/backend
|
||||||
|
SERVER_HOST: 111.170.19.33
|
||||||
|
SERVER_USER: root
|
||||||
|
CONTAINER_NAME: carrot_bbs
|
||||||
|
CONTAINER_NETWORK: 1panel-network
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ (github.event.pull_request.merged == true) || (github.event_name == 'workflow_dispatch') }}
|
||||||
|
environment: production
|
||||||
|
steps:
|
||||||
|
- name: Deploy via SSH
|
||||||
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
|
with:
|
||||||
|
host: ${{ env.SERVER_HOST }}
|
||||||
|
username: ${{ env.SERVER_USER }}
|
||||||
|
password: ${{ secrets.SERVER_PASSWORD }}
|
||||||
|
script: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.inputs.tag || 'latest' }}"
|
||||||
|
CONTAINER="${{ env.CONTAINER_NAME }}"
|
||||||
|
NETWORK="${{ env.CONTAINER_NETWORK }}"
|
||||||
|
|
||||||
|
echo "=== Pulling image: ${IMAGE} ==="
|
||||||
|
docker pull ${IMAGE}
|
||||||
|
|
||||||
|
echo "=== Stopping old container ==="
|
||||||
|
docker stop ${CONTAINER} 2>/dev/null || true
|
||||||
|
docker rm ${CONTAINER} 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "=== Starting new container ==="
|
||||||
|
docker run -d \
|
||||||
|
--name ${CONTAINER} \
|
||||||
|
--network ${NETWORK} \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 8080:8080 \
|
||||||
|
-p 50051:50051 \
|
||||||
|
-e APP_DATABASE_TYPE=postgres \
|
||||||
|
-e APP_DATABASE_POSTGRES_HOST=1Panel-postgresql-t0g7 \
|
||||||
|
-e APP_DATABASE_POSTGRES_PORT=5432 \
|
||||||
|
-e APP_DATABASE_POSTGRES_USER=carrot_bbs \
|
||||||
|
-e "APP_DATABASE_POSTGRES_PASSWORD=${{ secrets.DB_PASSWORD }}" \
|
||||||
|
-e APP_DATABASE_POSTGRES_DBNAME=carrot_bbs \
|
||||||
|
-e APP_REDIS_TYPE=redis \
|
||||||
|
-e APP_REDIS_REDIS_HOST=1Panel-redis-dfmM \
|
||||||
|
-e APP_REDIS_REDIS_PORT=6379 \
|
||||||
|
-e "APP_REDIS_REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }}" \
|
||||||
|
-e APP_REDIS_REDIS_DB=0 \
|
||||||
|
-e APP_S3_ENDPOINT=files.littlelan.cn \
|
||||||
|
-e "APP_S3_ACCESS_KEY=${{ secrets.S3_ACCESS_KEY }}" \
|
||||||
|
-e "APP_S3_SECRET_KEY=${{ secrets.S3_SECRET_KEY }}" \
|
||||||
|
-e APP_S3_BUCKET=test \
|
||||||
|
-e APP_S3_DOMAIN=files.littlelan.cn \
|
||||||
|
-e APP_S3_USE_SSL=true \
|
||||||
|
-e "APP_JWT_SECRET=${{ secrets.JWT_SECRET }}" \
|
||||||
|
-e APP_ENCRYPTION_ENABLED=true \
|
||||||
|
-e "APP_ENCRYPTION_KEY=${{ secrets.ENCRYPTION_KEY }}" \
|
||||||
|
-e APP_ENCRYPTION_KEY_VERSION=1 \
|
||||||
|
-e APP_GORSE_ENABLED=true \
|
||||||
|
-e APP_GORSE_ADDRESS=http://111.170.19.33:8088 \
|
||||||
|
-e "APP_GORSE_IMPORT_PASSWORD=${{ secrets.GORSE_IMPORT_PASSWORD }}" \
|
||||||
|
-e "APP_GORSE_EMBEDDING_API_KEY=${{ secrets.GORSE_EMBEDDING_API_KEY }}" \
|
||||||
|
-e APP_GORSE_EMBEDDING_URL=https://api.littlelan.cn/v1/embeddings \
|
||||||
|
-e APP_GORSE_EMBEDDING_MODEL=BAAI/bge-m3 \
|
||||||
|
-e APP_OPENAI_ENABLED=true \
|
||||||
|
-e APP_OPENAI_BASE_URL=https://api.littlelan.cn/ \
|
||||||
|
-e "APP_OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" \
|
||||||
|
-e APP_OPENAI_MODERATION_MODEL=qwen3.5-plus \
|
||||||
|
-e APP_OPENAI_MODERATION_MAX_IMAGES_PER_REQUEST=3 \
|
||||||
|
-e APP_OPENAI_REQUEST_TIMEOUT=30 \
|
||||||
|
-e APP_OPENAI_STRICT_MODERATION=false \
|
||||||
|
-e APP_EMAIL_ENABLED=true \
|
||||||
|
-e APP_EMAIL_HOST=smtp.exmail.qq.com \
|
||||||
|
-e APP_EMAIL_PORT=465 \
|
||||||
|
-e APP_EMAIL_USERNAME=no-reply@qczlit.cn \
|
||||||
|
-e "APP_EMAIL_PASSWORD=${{ secrets.EMAIL_PASSWORD }}" \
|
||||||
|
-e APP_EMAIL_FROM_ADDRESS=no-reply@qczlit.cn \
|
||||||
|
-e APP_EMAIL_FROM_NAME=Carrot_BBS \
|
||||||
|
-e APP_EMAIL_USE_TLS=true \
|
||||||
|
-e APP_EMAIL_TIMEOUT=15 \
|
||||||
|
-e APP_GRPC_ENABLED=true \
|
||||||
|
-e APP_GRPC_PORT=50051 \
|
||||||
|
-e APP_SERVER_MODE=release \
|
||||||
|
${IMAGE}
|
||||||
|
|
||||||
|
echo "=== Waiting for startup ==="
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
echo "=== Health check ==="
|
||||||
|
for i in 1 2 3; do
|
||||||
|
if curl -sf http://localhost:8080/health; then
|
||||||
|
echo "Health check passed!"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "Attempt $i failed, retrying..."
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=== Container logs ==="
|
||||||
|
docker logs ${CONTAINER} --tail 20
|
||||||
|
|
||||||
|
echo "=== Deployment completed ==="
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,4 +16,5 @@ logs/
|
|||||||
.env.*
|
.env.*
|
||||||
|
|
||||||
|
|
||||||
plans/
|
plans/
|
||||||
|
docs/
|
||||||
52
Dockerfile.new
Normal file
52
Dockerfile.new
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Build stage
|
||||||
|
FROM golang:1.22-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apk add --no-cache git ca-certificates tzdata
|
||||||
|
|
||||||
|
# Copy go mod files
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build binary
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o server ./cmd/server
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
# Install runtime dependencies
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& apt-get clean
|
||||||
|
|
||||||
|
# Create non-root user
|
||||||
|
RUN groupadd -r app && useradd -r -g app app
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy binary from builder
|
||||||
|
COPY --from=builder /build/server ./carrot_bbs
|
||||||
|
COPY --from=builder /build/configs ./configs
|
||||||
|
|
||||||
|
# Set ownership
|
||||||
|
RUN chown -R app:app /app
|
||||||
|
|
||||||
|
# Switch to non-root user
|
||||||
|
USER app
|
||||||
|
|
||||||
|
# Expose ports
|
||||||
|
EXPOSE 8080 50051
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
|
CMD curl -f http://localhost:8080/health || exit 1
|
||||||
|
|
||||||
|
# Run the binary
|
||||||
|
CMD ["./carrot_bbs"]
|
||||||
59
Makefile
Normal file
59
Makefile
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
.PHONY: build test run docker docker-push clean help
|
||||||
|
|
||||||
|
APP_NAME := carrot_bbs
|
||||||
|
VERSION := $(shell git rev-parse --short HEAD 2>/dev/null || echo "dev")
|
||||||
|
BUILD_TIME := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
|
||||||
|
LDFLAGS := -ldflags "-s -w -X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME)"
|
||||||
|
|
||||||
|
REGISTRY := code.littlelan.cn
|
||||||
|
IMAGE_NAME := carrot_bbs/backend
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Available targets:"
|
||||||
|
@echo " make build - Build the binary"
|
||||||
|
@echo " make test - Run tests"
|
||||||
|
@echo " make run - Run locally"
|
||||||
|
@echo " make docker - Build Docker image"
|
||||||
|
@echo " make docker-push - Push Docker image to registry"
|
||||||
|
@echo " make clean - Clean build artifacts"
|
||||||
|
|
||||||
|
build:
|
||||||
|
@echo "Building $(APP_NAME)..."
|
||||||
|
CGO_ENABLED=0 go build $(LDFLAGS) -o server ./cmd/server
|
||||||
|
|
||||||
|
test:
|
||||||
|
@echo "Running tests..."
|
||||||
|
go test -v -race -coverprofile=coverage.out ./...
|
||||||
|
go tool cover -html=coverage.out -o coverage.html
|
||||||
|
|
||||||
|
run:
|
||||||
|
@echo "Running $(APP_NAME)..."
|
||||||
|
go run ./cmd/server
|
||||||
|
|
||||||
|
docker:
|
||||||
|
@echo "Building Docker image..."
|
||||||
|
docker build -t $(REGISTRY)/$(IMAGE_NAME):$(VERSION) -t $(REGISTRY)/$(IMAGE_NAME):latest .
|
||||||
|
|
||||||
|
docker-push:
|
||||||
|
@echo "Pushing Docker image..."
|
||||||
|
docker push $(REGISTRY)/$(IMAGE_NAME):$(VERSION)
|
||||||
|
docker push $(REGISTRY)/$(IMAGE_NAME):latest
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo "Cleaning..."
|
||||||
|
rm -f server
|
||||||
|
rm -f coverage.out coverage.html
|
||||||
|
go clean
|
||||||
|
|
||||||
|
lint:
|
||||||
|
@echo "Running linter..."
|
||||||
|
golangci-lint run ./...
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
@echo "Formatting code..."
|
||||||
|
go fmt ./...
|
||||||
|
goimports -w .
|
||||||
|
|
||||||
|
wire:
|
||||||
|
@echo "Generating wire..."
|
||||||
|
cd cmd/server && wire
|
||||||
Reference in New Issue
Block a user