Compare commits

..

4 Commits

Author SHA1 Message Date
xiaoxia f098eabc12 Merge pull request 'feat(deploy): 添加环境配置模板 + 修复 compose 路径 bug' (#1628) from feat/config-templates-phase2 into develop
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 4s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 3m46s
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 5m13s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 5m28s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 4s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 7m34s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 8m4s
AI Code Review / AI Code Review (pull_request) Successful in 8m53s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 0s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 2s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m16s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m41s
CI/CD Pipeline / Build Staging API Image (push) Successful in 4m4s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m52s
CI/CD Pipeline / Integration Tests (push) Successful in 5m40s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m37s
CI/CD Pipeline / Validate - Style (push) Successful in 6m18s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 1m13s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m14s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m45s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 8m41s
CI/CD Pipeline / Validate - Security (push) Successful in 11m23s
CI/CD Pipeline / Unit Tests (push) Successful in 14m2s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
feat: 配置模板 Phase 2 - 环境配置模板 + compose.yml 修复 (#1628)

- 新增 deploy/configs/.env.staging 和 .env.production 配置模板
- 新增 deploy/configs/nginx-staging.conf 和 nginx-production.conf
- 修复 compose.yml GENERATED_FILES_HOST_DIR 强制显式设置
- .gitignore 添加 deploy/configs/.env 例外

Co-authored-by: 后端编程 Agent
2026-09-02 16:35:20 +08:00
xiaoxia a633b64d4f feat(migrations): 对齐 staging 数据库结构与生产 (#1627)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Failing after 3s
CI/CD Pipeline / Check push changed paths (push) Successful in 10s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m3s
CI/CD Pipeline / Build Staging API Image (push) Successful in 3m32s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m4s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Style (push) Successful in 4m21s
CI/CD Pipeline / Integration Tests (push) Successful in 5m23s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m33s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 1m15s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m16s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m56s
CI/CD Pipeline / Validate - Security (push) Successful in 8m54s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 10m12s
CI/CD Pipeline / Unit Tests (push) Successful in 15m31s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-02 15:30:38 +08:00
xiaoxia 7eecabaf3d feat(deploy): 添加环境配置模板 + 修复 compose 路径 bug
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Failing after 2s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m51s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m45s
AI Code Review / AI Code Review (pull_request) Failing after 4m30s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m57s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m6s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 5m34s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 5m46s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 6m50s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 9m5s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 17m48s
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Failing after 2s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 4m22s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 5m37s
- 新增 deploy/configs/.env.staging — Staging 环境配置模板(含 MinIO)
- 新增 deploy/configs/.env.production — Production 环境配置模板(无 MinIO)
- 新增 deploy/configs/nginx-staging.conf — Staging Nginx 配置
- 新增 deploy/configs/nginx-production.conf — Production Nginx 配置
- 修复 infra/docker/compose.yml: GENERATED_FILES_HOST_DIR 去掉默认值,
  改为 ?: 语法强制要求显式设置,防止 Production 静默指向 staging 目录
- 更新 .gitignore: 为 deploy/configs/ 下的模板文件添加例外
2026-09-02 15:25:27 +08:00
xiaoxia 5fb9975913 feat(assets): 素材库页面加载时自动创建默认 video 库 (#1626)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Check push changed paths (push) Successful in 4s
CI/CD Pipeline / Build Staging API Image (push) Successful in 4m34s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 4m43s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m48s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Style (push) Successful in 5m37s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 6m13s
CI/CD Pipeline / Integration Tests (push) Successful in 6m30s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m38s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 2m3s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m20s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 9m26s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 3m28s
CI/CD Pipeline / Validate - Security (push) Successful in 10m29s
CI/CD Pipeline / Unit Tests (push) Successful in 17m0s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-02 15:00:34 +08:00
15 changed files with 648 additions and 267 deletions
+2 -3
View File
@@ -1412,9 +1412,9 @@ jobs:
- validate-security
- validate-python
- unit-tests
- frontend-lint
- frontend-unit-test
if: |
(startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'push' && github.ref_name == 'main')) && !failure() && !cancelled()
if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'push' && github.ref_name == 'main')
strategy:
fail-fast: false
matrix:
@@ -2068,4 +2068,3 @@ jobs:
START_TIME=""
[ -f /tmp/ci_job_start_time ] && START_TIME=$(cat /tmp/ci_job_start_time)
curl -sfH "Authorization: token ${GITHUB_TOKEN:-$GITEA_TOKEN}" -o /tmp/_ci_trace.py "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/ci_trace_report.py?ref=${GITHUB_SHA}" 2>/dev/null && python3 /tmp/_ci_trace.py --service xiaoxia-saas-ci --status $STATUS --start-time "$START_TIME" || true
# CI retry trigger
+3
View File
@@ -24,6 +24,9 @@ ruff_cache/
.env.production
.env.staging
!.env.example
# 配置模板不受忽略规则限制
!deploy/configs/.env.staging
!deploy/configs/.env.production
# OS / editor
.DS_Store
-1
View File
@@ -1 +0,0 @@
retrigger3
-1
View File
@@ -263,4 +263,3 @@ pytest --cov=packages --cov-report=html
---
**License**: MIT
<!-- CI trigger: 1788229339 -->
@@ -0,0 +1,26 @@
"""add sort_order to template_categories
Revision ID: 061_sort_order
Revises: 060_migrate_segments
Create Date: 2026-09-02
"""
import sqlalchemy as sa
from alembic import op
revision = "061_sort_order"
down_revision = "060_migrate_segments"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"template_categories",
sa.Column("sort_order", sa.Integer, nullable=False, server_default="0"),
)
def downgrade() -> None:
op.drop_column("template_categories", "sort_order")
@@ -0,0 +1,28 @@
"""re-add edit_plan_id to generation_tasks (align staging with production)
Revision ID: 062_edit_plan_id
Revises: 061_sort_order
Create Date: 2026-09-02
"""
import sqlalchemy as sa
from alembic import op
revision = "062_edit_plan_id"
down_revision = "061_sort_order"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"generation_tasks",
sa.Column("edit_plan_id", sa.String(36), nullable=True),
)
op.create_index("ix_generation_tasks_edit_plan_id_2", "generation_tasks", ["edit_plan_id"])
def downgrade() -> None:
op.drop_index("ix_generation_tasks_edit_plan_id_2", table_name="generation_tasks")
op.drop_column("generation_tasks", "edit_plan_id")
@@ -3,9 +3,11 @@ import { useQuery } from "@tanstack/react-query"
import {
getAssetLibraries,
getAssets,
ensureDefaultLibrary,
type AssetLibraryItem,
type AssetItem as ApiAssetItem,
} from "@/api/assets"
import { getOrCreateDefaultProject } from "@/api/projects"
import { mapLibrary, mapAsset, type AssetItem, type LibraryItem } from "../types"
/**
@@ -16,7 +18,18 @@ export function useAssetsData() {
/* ── 视频库列表查询 ── */
const { data: apiLibraries = [], isLoading: libLoading } = useQuery<AssetLibraryItem[], Error>({
queryKey: ["asset-libraries"],
queryFn: getAssetLibraries,
queryFn: async () => {
const libs = await getAssetLibraries()
// 如果没有 video 类型的库,自动创建默认视频素材库(与 useVoiceMaterials 保持一致)
const hasVideoLib = libs.some((lib) => lib.kind === "video")
if (!hasVideoLib) {
const project = await getOrCreateDefaultProject()
await ensureDefaultLibrary({ project_id: project.id, kind: "video" })
// 创建后重新拉取最新列表
return getAssetLibraries()
}
return libs
},
staleTime: 60_000,
})
+227
View File
@@ -0,0 +1,227 @@
# ============================================================
# 小虾 SaaS — Production 环境配置模板
# ============================================================
# 使用方式:复制为 /var/lib/xiaoxia-saas-production/.env 并填入实际密钥
# 敏感值标记为 ${PLACEHOLDER},部署前必须替换为真实值
# ============================================================
# ==================== 应用基本配置 ====================
# 应用名称
APP_NAME=xiaoxia-saas
# 环境标识
APP_ENV=production
# 关闭 Debug 模式
DEBUG=false
# 应用基础 URL(前端页面地址)
APP_BASE_URL=https://xiaoxiajianji.com
# 对外公开的 API 基础 URL(用于生成回调链接等)
PUBLIC_API_BASE_URL=https://api.xiaoxiajianji.com
# API 服务监听地址
API_HOST=0.0.0.0
# API 服务监听端口
API_PORT=8001
# 生产环境关闭自动建表,使用 alembic migration
AUTO_CREATE_SCHEMA=false
# ==================== 数据库配置 ====================
# 数据库连接串(格式:postgresql+psycopg://user:password@host:port/dbname
# ${DATABASE_URL} — 替换为实际的 Production PostgreSQL 连接串
DATABASE_URL=${DATABASE_URL}
# 连接池大小(常驻连接数)
DATABASE_POOL_SIZE=20
# 连接池最大溢出连接数(pool_size + max_overflow = 最大并发连接数)
DATABASE_MAX_OVERFLOW=10
# 获取连接超时时间(秒)
DATABASE_POOL_TIMEOUT=30
# 连接回收时间(秒),防止数据库端主动断开导致的死连接
DATABASE_POOL_RECYCLE=3600
# 不使用内存数据库
USE_IN_MEMORY_DB=false
# ==================== Redis 配置 ====================
# Redis 连接 URL(格式:redis://[:password@]host:port/db
# ${REDIS_URL} — 替换为实际的 Production Redis 连接串
REDIS_URL=${REDIS_URL}
# 启用 Redis Session 存储(多实例部署必须开启)
ENABLE_REDIS_SESSIONS=true
# ==================== Celery 任务队列 ====================
# Celery Broker(任务分发),使用 Redis db0
CELERY_BROKER_URL=${CELERY_BROKER_URL}
# Celery Result Backend(任务结果存储),使用 Redis db1
CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND}
# ==================== Worker 配置 ====================
# Worker 进程名称
WORKER_NAME=xiaoxia-saas-worker
# Worker 并发数(同时执行的任务数)
WORKER_CONCURRENCY=4
# 每个子进程最多处理多少任务后重启(防止内存泄漏)
WORKER_MAX_TASKS_PER_CHILD=1000
# ==================== JWT 认证配置 ====================
# JWT 签名密钥 — 必须设置为强随机字符串(至少32字符)
# ${JWT_SECRET_KEY} — 替换为实际的随机密钥
JWT_SECRET_KEY=${JWT_SECRET_KEY}
# JWT 签名算法
JWT_ALGORITHM=HS256
# Access Token 过期时间(分钟)
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=30
# Refresh Token 过期时间(天)
JWT_REFRESH_TOKEN_EXPIRE_DAYS=30
# ==================== 邮件配置 ====================
# 启用邮件投递
ENABLE_EMAIL_DELIVERY=true
# SMTP 服务器地址
SMTP_HOST=smtp.gmail.com
# SMTP 端口
SMTP_PORT=587
# SMTP 用户名
# ${SMTP_USER} — 替换为实际的 SMTP 用户名
SMTP_USER=${SMTP_USER}
# SMTP 密码 / 应用专用密码
# ${SMTP_PASSWORD} — 替换为实际的 SMTP 密码
SMTP_PASSWORD=${SMTP_PASSWORD}
# 发件人邮箱
SMTP_FROM_EMAIL=noreply@xiaoxia-saas.com
# 发件人显示名称
SMTP_FROM_NAME=小虾 SaaS
# 启用 TLS
SMTP_USE_TLS=true
# ==================== 阿里云 OSS 配置 ====================
# OSS 区域 endpoint
OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
# OSS Access Key ID
# ${OSS_ACCESS_KEY_ID} — 替换为实际的 OSS Access Key ID
OSS_ACCESS_KEY_ID=${OSS_ACCESS_KEY_ID}
# OSS Access Key Secret
# ${OSS_ACCESS_KEY_SECRET} — 替换为实际的 OSS Access Key Secret
OSS_ACCESS_KEY_SECRET=${OSS_ACCESS_KEY_SECRET}
# OSS Bucket 名称
OSS_BUCKET_NAME=xiaoxia-autocut
# 直传最大文件大小(MB
OSS_DIRECT_UPLOAD_MAX_MB=2000
# 直传签名有效期(秒)
OSS_DIRECT_UPLOAD_EXPIRE_SECONDS=900
# ==================== CORS 配置 ====================
# 允许跨域的前端域名列表,逗号分隔
CORS_ORIGINS_RAW=https://xiaoxiajianji.com,https://www.xiaoxiajianji.com
# ==================== 生成文件路径 ====================
# 容器内生成文件目录(固定值,勿改)
GENERATED_FILES_DIR=/app/generated
# 生成文件 URL 前缀
GENERATED_FILES_URL_PREFIX=/generated-files
# 主机上生成文件目录(供 Docker volume bind mount 使用)
GENERATED_FILES_HOST_DIR=/var/lib/xiaoxia-saas-production/generated
# ==================== 渲染引擎配置 ====================
# 渲染引擎选择:legacy(旧引擎,稳定)/ unified(新架构)
RENDER_ENGINE=legacy
# ==================== CosyVoice 语音合成 ====================
# 阿里云百灵语音合成服务 API Key
# ${COSYVOICE_API_KEY} — 替换为实际的 CosyVoice API Key
COSYVOICE_API_KEY=${COSYVOICE_API_KEY}
# API 基础 URL
COSYVOICE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
# 模型选择:cosyvoice-v3-flash(推荐)/ cosyvoice-v3-plus
COSYVOICE_MODEL=cosyvoice-v3-flash
# 音色:v3 系列系统音色带 _v3 后缀
COSYVOICE_VOICE=longxiaochun_v3
# 采样率
COSYVOICE_SAMPLE_RATE=22050
# 输出格式
COSYVOICE_FORMAT=mp3
# 音色克隆模型名(固定值)
COSYVOICE_CLONE_MODEL=voice-enrollment
# ==================== 豆包大模型(火山引擎方舟)====================
# 豆包 API Key
# ${DOUBAO_API_KEY} — 替换为实际的豆包 API Key
DOUBAO_API_KEY=${DOUBAO_API_KEY}
# 模型名称
DOUBAO_MODEL=doubao-seed-1-6-250615
# API 基础 URL
DOUBAO_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
# 请求超时时间(秒)
DOUBAO_TIMEOUT=30
# 最大重试次数
DOUBAO_MAX_RETRIES=2
# ==================== 监控(可选)====================
# Sentry DSN(取消注释并填入实际值以启用错误追踪)
# SENTRY_DSN=${SENTRY_DSN}
+244
View File
@@ -0,0 +1,244 @@
# ============================================================
# 小虾 SaaS — Staging 环境配置模板
# ============================================================
# 使用方式:复制为 /var/lib/xiaoxia-saas-staging/.env 并填入实际密钥
# 敏感值标记为 ${PLACEHOLDER},部署前必须替换为真实值
# ============================================================
# ==================== 应用基本配置 ====================
# 应用名称
APP_NAME=xiaoxia-saas
# 环境标识
APP_ENV=staging
# 关闭 Debug 模式
DEBUG=false
# 应用基础 URL(前端页面地址)
APP_BASE_URL=https://staging.xiaoxiajianji.com
# 对外公开的 API 基础 URL(用于生成回调链接等)
PUBLIC_API_BASE_URL=https://staging-api.xiaoxiajianji.com
# API 服务监听地址
API_HOST=0.0.0.0
# API 服务监听端口
API_PORT=8000
# 生产/预发布环境关闭自动建表,使用 alembic migration
AUTO_CREATE_SCHEMA=false
# ==================== 数据库配置 ====================
# 数据库连接串(格式:postgresql+psycopg://user:password@host:port/dbname
# ${DATABASE_URL} — 替换为实际的 Staging PostgreSQL 连接串
DATABASE_URL=${DATABASE_URL}
# 连接池大小(常驻连接数)
DATABASE_POOL_SIZE=20
# 连接池最大溢出连接数(pool_size + max_overflow = 最大并发连接数)
DATABASE_MAX_OVERFLOW=10
# 获取连接超时时间(秒)
DATABASE_POOL_TIMEOUT=30
# 连接回收时间(秒),防止数据库端主动断开导致的死连接
DATABASE_POOL_RECYCLE=3600
# 不使用内存数据库
USE_IN_MEMORY_DB=false
# ==================== Redis 配置 ====================
# Redis 连接 URL(格式:redis://[:password@]host:port/db
# ${REDIS_URL} — 替换为实际的 Staging Redis 连接串
REDIS_URL=${REDIS_URL}
# 启用 Redis Session 存储(多实例部署必须开启)
ENABLE_REDIS_SESSIONS=true
# ==================== Celery 任务队列 ====================
# Celery Broker(任务分发),使用 Redis db0
CELERY_BROKER_URL=${CELERY_BROKER_URL}
# Celery Result Backend(任务结果存储),使用 Redis db1
CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND}
# ==================== Worker 配置 ====================
# Worker 进程名称
WORKER_NAME=xiaoxia-saas-worker
# Worker 并发数(同时执行的任务数)
WORKER_CONCURRENCY=1
# 每个子进程最多处理多少任务后重启(防止内存泄漏)
WORKER_MAX_TASKS_PER_CHILD=1000
# ==================== JWT 认证配置 ====================
# JWT 签名密钥 — 必须设置为强随机字符串(至少32字符)
# ${JWT_SECRET_KEY} — 替换为实际的随机密钥
JWT_SECRET_KEY=${JWT_SECRET_KEY}
# JWT 签名算法
JWT_ALGORITHM=HS256
# Access Token 过期时间(分钟)
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=30
# Refresh Token 过期时间(天)
JWT_REFRESH_TOKEN_EXPIRE_DAYS=30
# ==================== 邮件配置 ====================
# 启用邮件投递
ENABLE_EMAIL_DELIVERY=true
# SMTP 服务器地址
SMTP_HOST=smtp.gmail.com
# SMTP 端口
SMTP_PORT=587
# SMTP 用户名
# ${SMTP_USER} — 替换为实际的 SMTP 用户名
SMTP_USER=${SMTP_USER}
# SMTP 密码 / 应用专用密码
# ${SMTP_PASSWORD} — 替换为实际的 SMTP 密码
SMTP_PASSWORD=${SMTP_PASSWORD}
# 发件人邮箱
SMTP_FROM_EMAIL=noreply@xiaoxia-saas.com
# 发件人显示名称
SMTP_FROM_NAME=小虾 SaaS
# 启用 TLS
SMTP_USE_TLS=true
# ==================== 阿里云 OSS 配置 ====================
# OSS 区域 endpoint
OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
# OSS Access Key ID
# ${OSS_ACCESS_KEY_ID} — 替换为实际的 OSS Access Key ID
OSS_ACCESS_KEY_ID=${OSS_ACCESS_KEY_ID}
# OSS Access Key Secret
# ${OSS_ACCESS_KEY_SECRET} — 替换为实际的 OSS Access Key Secret
OSS_ACCESS_KEY_SECRET=${OSS_ACCESS_KEY_SECRET}
# OSS Bucket 名称
OSS_BUCKET_NAME=xiaoxia-autocut
# 直传最大文件大小(MB
OSS_DIRECT_UPLOAD_MAX_MB=2000
# 直传签名有效期(秒)
OSS_DIRECT_UPLOAD_EXPIRE_SECONDS=900
# ==================== MinIO 配置(Staging 独有)====================
# Staging 环境使用 MinIO 替代 OSS 进行文件存储测试
# MinIO 服务 Endpoint
# ${MINIO_ENDPOINT} — 替换为实际的 MinIO 地址
MINIO_ENDPOINT=${MINIO_ENDPOINT}
# MinIO Access Key
# ${MINIO_ACCESS_KEY} — 替换为实际的 MinIO Access Key
MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
# MinIO Secret Key
# ${MINIO_SECRET_KEY} — 替换为实际的 MinIO Secret Key
MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
# MinIO Bucket 名称
MINIO_BUCKET=${MINIO_BUCKET}
# 是否使用 SSL 连接 MinIO
MINIO_USE_SSL=false
# ==================== CORS 配置 ====================
# 允许跨域的前端域名列表,逗号分隔
CORS_ORIGINS_RAW=https://staging.xiaoxiajianji.com,https://staging-web.xiaoxiajianji.com
# ==================== 生成文件路径 ====================
# 容器内生成文件目录(固定值,勿改)
GENERATED_FILES_DIR=/app/generated
# 生成文件 URL 前缀
GENERATED_FILES_URL_PREFIX=/generated-files
# 主机上生成文件目录(供 Docker volume bind mount 使用)
GENERATED_FILES_HOST_DIR=/var/lib/xiaoxia-saas-staging/generated
# ==================== 渲染引擎配置 ====================
# 渲染引擎选择:legacy(旧引擎,稳定)/ unified(新架构)
RENDER_ENGINE=legacy
# ==================== CosyVoice 语音合成 ====================
# 阿里云百灵语音合成服务 API Key
# ${COSYVOICE_API_KEY} — 替换为实际的 CosyVoice API Key
COSYVOICE_API_KEY=${COSYVOICE_API_KEY}
# API 基础 URL
COSYVOICE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
# 模型选择:cosyvoice-v3-flash(推荐)/ cosyvoice-v3-plus
COSYVOICE_MODEL=cosyvoice-v3-flash
# 音色:v3 系列系统音色带 _v3 后缀
COSYVOICE_VOICE=longxiaochun_v3
# 采样率
COSYVOICE_SAMPLE_RATE=22050
# 输出格式
COSYVOICE_FORMAT=mp3
# 音色克隆模型名(固定值)
COSYVOICE_CLONE_MODEL=voice-enrollment
# ==================== 豆包大模型(火山引擎方舟)====================
# 豆包 API Key
# ${DOUBAO_API_KEY} — 替换为实际的豆包 API Key
DOUBAO_API_KEY=${DOUBAO_API_KEY}
# 模型名称
DOUBAO_MODEL=doubao-seed-1-6-250615
# API 基础 URL
DOUBAO_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
# 请求超时时间(秒)
DOUBAO_TIMEOUT=30
# 最大重试次数
DOUBAO_MAX_RETRIES=2
+51
View File
@@ -0,0 +1,51 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml+rss;
client_max_body_size 800m;
# SPA routing - index.html 禁止缓存,确保每次获取最新版本
location / {
try_files $uri /index.html;
}
# API proxy — Production 环境代理到 production API 容器
resolver 127.0.0.11 valid=10s;
resolver_timeout 5s;
location /api/ {
proxy_pass http://xiaoxia-api-production:8000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_request_buffering off;
}
# Generated files — 通过 alias 映射容器内 /app/generated/ 目录
location /generated-files/ {
alias /app/generated/;
}
# Assets with legacy fallback — 部署期间兼容旧版缓存的 hash 文件名
# 先在当前镜像中找,找不到去 legacy-assets 目录找(从旧版本容器中备份的)
location ^~ /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri /assets-legacy$uri =404;
}
# 静态资源长缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
+50
View File
@@ -0,0 +1,50 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml+rss;
client_max_body_size 800m;
# SPA routing - index.html 禁止缓存,确保每次获取最新版本
location = /index.html {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
expires 0;
}
# SPA fallback
location / {
try_files $uri /index.html;
}
# API proxy — Staging 环境代理到 staging API 容器
resolver 127.0.0.11 valid=10s;
resolver_timeout 5s;
location /api/ {
proxy_pass http://xiaoxia-api-staging:8000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_request_buffering off;
}
# Generated files — 通过 alias 映射容器内 /app/generated/ 目录
location /generated-files/ {
alias /app/generated/;
}
# 静态资源长缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
+1 -1
View File
@@ -208,7 +208,7 @@ volumes:
# 重要: 确保主机目录存在且有正确权限
# Staging: /var/lib/xiaoxia-saas-staging/generated
# Production: /var/lib/xiaoxia-saas-production/generated
device: ${GENERATED_FILES_HOST_DIR:-/var/lib/xiaoxia-saas-staging/generated}
device: ${GENERATED_FILES_HOST_DIR:?GENERATED_FILES_HOST_DIR must be set in .env}
# ===========================================
# 网络配置
@@ -289,6 +289,7 @@ class GenerationTaskModel(Base):
completed_at = Column(DateTime, nullable=True)
created_by_user_id = Column(String(36), nullable=False, default="", index=True)
source_edit_plan_id = Column(String(36), nullable=True, index=True)
edit_plan_id = Column(String(36), nullable=True, index=True)
asset_select_mode = Column(String(20), nullable=False, default="")
batch_id = Column(String(36), nullable=False, default="", index=True)
video_title = Column(String(255), nullable=False, default="")
@@ -497,6 +498,7 @@ class TemplateCategoryModel(Base):
id = Column(String(36), primary_key=True)
user_id = Column(String(36), nullable=False, index=True)
name = Column(String(100), nullable=False)
sort_order = Column(Integer, nullable=False, default=0)
created_at = Column(DateTime, nullable=False, default=lambda: datetime.now(timezone.utc))
-103
View File
@@ -1,103 +0,0 @@
#!/bin/bash
# ============================================
# 基础镜像同步脚本 - 从公共镜像源同步到私有ACR
# 用法:
# ACR_USERNAME=xxx ACR_PASSWORD=yyy bash scripts/ci/sync_base_images.sh
# ============================================
set -euo pipefail
ACR_REGISTRY="${ACR_REGISTRY:-xiaoxia-registry.cn-hangzhou.cr.aliyuncs.com/xiaoxiakeji}"
ACR_USERNAME="${ACR_USERNAME:-}"
ACR_PASSWORD="${ACR_PASSWORD:-}"
SOURCE_PREFIX="${SOURCE_PREFIX:-docker.m.daocloud.io/library}"
# 需要同步的镜像列表 (源镜像名:tag => ACR目标名:tag)
IMAGES=(
"python:3.12-slim-bookworm"
"python:3.12-slim"
"node:20"
"nginx:alpine"
)
echo "============================================"
echo " 基础镜像同步到 ACR"
echo " ACR: $ACR_REGISTRY"
echo " 源: $SOURCE_PREFIX"
echo "============================================"
echo ""
# 登录 ACR
if [ -n "$ACR_PASSWORD" ] && [ -n "$ACR_USERNAME" ]; then
echo "登录 ACR..."
ACR_HOST=$(echo "$ACR_REGISTRY" | cut -d/ -f1)
printf '%s' "$ACR_PASSWORD" | docker login "$ACR_HOST" -u "$ACR_USERNAME" --password-stdin
echo "ACR 登录成功"
echo ""
fi
success=0
failed=0
for image in "${IMAGES[@]}"; do
source_image="${SOURCE_PREFIX}/${image}"
target_image="${ACR_REGISTRY}/base/${image}"
echo "--- 同步: $image ---"
echo " 源: $source_image"
echo " 目标: $target_image"
# Pull 源镜像(带重试)
pulled=0
for attempt in 1 2 3; do
echo " Pull 尝试 $attempt/3..."
if docker pull "$source_image"; then
pulled=1
break
fi
echo " Pull 失败,5s 后重试..."
sleep 5
done
if [ "$pulled" -eq 0 ]; then
echo " ❌ Pull 失败: $image"
failed=$((failed + 1))
continue
fi
# Tag
docker tag "$source_image" "$target_image"
echo " Tag 完成"
# Push 到 ACR
pushed=0
for attempt in 1 2 3; do
echo " Push 尝试 $attempt/3..."
if docker push "$target_image"; then
pushed=1
break
fi
echo " Push 失败,5s 后重试..."
sleep 5
done
if [ "$pushed" -eq 1 ]; then
echo " ✅ 同步成功: $image"
success=$((success + 1))
else
echo " ❌ Push 失败: $image"
failed=$((failed + 1))
fi
echo ""
done
echo "============================================"
echo " 同步完成"
echo " 成功: $success"
echo " 失败: $failed"
echo "============================================"
if [ "$failed" -gt 0 ]; then
exit 1
fi
-157
View File
@@ -1,157 +0,0 @@
#!/bin/bash
# CI Validate: 代码质量与安全扫描(并行Job 1/3)
# 包含:密钥扫描、格式检查、安全扫描、依赖漏洞、死代码检测、脚本语法校验
set -eu
echo "=== CI Validate: 代码质量与安全扫描 ==="
# --- 密钥检测 ---
echo ""
echo "=== [1/6] Secret detection (detect-secrets) ==="
python3 -m pip install -q detect-secrets
detect-secrets --version
detect-secrets scan \
--all-files \
--exclude-files '(^|/)(tests|test|e2e|__tests__|spec|docs|node_modules|site-packages|migrations|alembic|.gitea|.git|.pytest_cache|.next|dist|build)/' \
--exclude-files '\.(md|rst|txt|lock|example|sample|min\.js|min\.css|spec\.ts|test\.ts|test\.py)$' \
--exclude-files '(package-lock|yarn\.lock|poetry\.lock|Pipfile\.lock)$' \
--disable-plugin Base64HighEntropyString \
--disable-plugin HexHighEntropyString \
--disable-plugin BasicAuthDetector \
--disable-plugin KeywordDetector \
--disable-plugin IPPublicDetector \
> /tmp/secrets-scan.json 2>&1
FOUND=$(python3 -c "
import json
try:
with open('/tmp/secrets-scan.json') as f:
data = json.load(f)
results = data.get('results', {})
total = sum(len(v) for v in results.values())
print(total)
except Exception:
print('error')
")
echo "Secrets detected: $FOUND"
if [ "$FOUND" != "0" ] && [ "$FOUND" != "error" ]; then
echo ""
echo "=== Secret details ==="
python3 -c "
import json
with open('/tmp/secrets-scan.json') as f:
data = json.load(f)
for fpath, items in data.get('results', {}).items():
for item in items:
line = item.get('line_number', '?')
stype = item.get('type', '?')
hashed = item.get('hashed_secret', '')[:16]
print(f' {fpath}:{line} [{stype}] {hashed}...')
"
echo ""
echo "ERROR: Potential secrets detected in code!"
exit 1
fi
echo "✅ Secret scan passed"
# --- 代码质量检查(全量,PR 和 push 统一标准)---
# 历史:PR 侧用增量检查以加速,但会导致 push 侧全量检查失败时 PR 侧感知不到
# 现在统一全量检查,确保 CI 真正保护主分支(black/isort/ruff 全量仅多几十秒)
echo ""
echo "=== [2/6] Code quality checks (full scan) ==="
SCAN_MODE="full"
echo "Full scan mode"
python3 -m compileall -q alembic apps packages tests scripts
python3 -m black --check --fast alembic apps packages tests scripts
python3 -m isort --check-only alembic apps packages tests scripts
python3 -m ruff check apps packages tests --statistics
echo "✅ Code quality checks passed"
# --- Bandit 安全扫描(仅告警) ---
echo ""
echo "=== [3/6] Security scan (bandit, advisory only) ==="
set +e
bandit -r apps packages -q -ll
BANDIT_EXIT=$?
set -e
if [ "$BANDIT_EXIT" -ne 0 ]; then
echo "⚠️ Bandit found security issues (advisory mode - not blocking CI)"
else
echo "✅ Bandit security scan passed"
fi
# --- Pip-audit 依赖漏洞扫描(仅告警) ---
echo ""
echo "=== [4/6] Python dependency vulnerability scan (pip-audit, advisory only) ==="
python3 -m pip install -q pip-audit
pip-audit --version
EXIT_CODE=0
for req_file in requirements.txt requirements-base.txt requirements-dev.txt; do
if [ -f "$req_file" ]; then
echo "--- Scanning $req_file ---"
pip-audit -r "$req_file" --desc on 2>&1 | head -40 || EXIT_CODE=$?
echo ""
fi
done
echo "pip-audit scan completed (advisory mode - warnings only, not blocking CI)"
# --- Vulture 死代码检测(仅告警) ---
echo ""
echo "=== [5/6] Dead code detection (vulture, advisory only) ==="
set +e
python3 -m pip install -q vulture
vulture --version
echo "告警模式,不阻断CI。置信度>=90%建议尽快确认。"
echo ""
vulture apps packages scripts \
--exclude "tests,test,migrations,.gitea,docs,node_modules,site-packages,*/test_*.py,*/conftest.py" \
--min-confidence 70 \
2>&1 | sort -t'(' -k2 -rn | head -80
echo ""
echo "=== vulture scan summary ==="
echo "发现潜在死代码(可能包含框架装饰器注册的函数,为误报)"
echo "建议:定期人工审查高置信度(>=90%)条目"
set -e
# --- CI脚本语法校验 ---
echo ""
echo "=== [6/6] CI & shell scripts syntax validation ==="
SYNTAX_ERROR=0
# 检查所有 CI shell 脚本
for script in scripts/ci/*.sh; do
if [ -f "$script" ]; then
if ! bash -n "$script" 2>&1; then
echo "❌ 语法错误: $script"
SYNTAX_ERROR=1
fi
fi
done
# 检查所有 CI Python 脚本语法
for script in scripts/ci/*.py; do
if [ -f "$script" ]; then
if ! python3 -m py_compile "$script" 2>&1; then
echo "❌ Python语法错误: $script"
SYNTAX_ERROR=1
fi
fi
done
# 检查 .gitea/workflows 下的脚本(如果有)
for script in .gitea/workflows/*.sh; do
if [ -f "$script" ]; then
if ! bash -n "$script" 2>&1; then
echo "❌ 语法错误: $script"
SYNTAX_ERROR=1
fi
fi
done
if [ "$SYNTAX_ERROR" -ne 0 ]; then
echo "❌ CI脚本语法校验失败,见上方错误"
exit 1
fi
echo "✅ All CI scripts syntax OK"
echo ""
echo "=== CI Validate: 代码质量与安全扫描 全部通过 ✅ ==="