Files
metapi/.github/workflows/ci.yml
T
puyujian 569a15e14c feat: enhance API key management and fix routing logic
* ```
feat(accounts): 支持批量添加API密钥连接

- 添加accessTokens字段到账户创建载荷模式,允许传递多个API密钥
- 实现批量API密钥解析功能,支持换行符、空格和逗号分隔
- 重构账户创建逻辑以支持单个和批量API密钥处理
- 在前端界面中添加批量API密钥输入检测和处理
- 添加批量创建结果返回和错误处理
- 更新验证流程以支持批量模式下的逐条验证
- 添加详细的批量API密钥测试用例

该更改允许用户一次提交多个API密钥,系统将为每个密钥创建独立的连接并参与负载均衡。
```

* ```
fix(tokenRouter): 调整路由匹配优先级以优先匹配显示名称别名

当精确路由和显式组显示名称冲突时,现在优先选择显式组显示名称
而不是精确路由模式。这修正了路由决策逻辑,使显式组路由能够正
确覆盖精确模型匹配。

相关的测试用例已更新以反映新的匹配行为,确保显式组路由在冲
突情况下获得更高优先级。
```

* ```
fix(accounts): 修复API密钥验证错误消息显示逻辑

当凭证模式不为apikey时显示会话令牌提示信息,否则显示API密钥验证失败信息

feat(accounts): 更新API密钥输入界面提示文本

将批量添加说明从输入框占位符移至独立提示文本,
使界面更清晰易懂
```

* ```
feat(workflow): 在CI/CD工作流中使用动态Docker Hub镜像名称

- 在ci.yml和release.yml工作流中添加DOCKERHUB_IMAGE环境变量
- 将硬编码的Docker Hub用户名替换为从secrets.DOCKERHUB_USERNAME动态获取
- 更新docker.metadata-action配置以使用环境变量中的镜像名称
- 添加测试用例验证Docker Hub镜像名称的正确配置
```

* fix(accounts): 修复批量创建回归并下沉创建流程
2026-04-07 14:28:25 +08:00

474 lines
12 KiB
YAML

name: CI
on:
push:
branches: [main, master]
paths-ignore:
- 'docs/**'
- 'README.md'
- 'README_EN.md'
- 'CONTRIBUTING.md'
- 'SECURITY.md'
- 'CODE_OF_CONDUCT.md'
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: 25
# Non-desktop CI jobs do not need the Electron runtime binary.
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
jobs:
changes:
name: Detect Docs Changes
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
outputs:
docs: ${{ steps.filter.outputs.docs }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Detect docs-related changes
id: filter
uses: dorny/paths-filter@v4
with:
filters: |
docs:
- 'docs/**'
- 'README.md'
- 'README_EN.md'
- 'CONTRIBUTING.md'
- 'SECURITY.md'
- 'CODE_OF_CONDUCT.md'
- '.github/workflows/docs-pages.yml'
test-core:
name: Test Core
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Run tests
env:
DB_PARITY_SKIP_LIVE_SCHEMA: 'true'
run: npm test
build-web:
name: Build Web
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Build web
run: npm run build:web
build-server:
name: Build Server
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Build server
run: npm run build:server
build-desktop:
name: Build Desktop
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
env:
ELECTRON_SKIP_BINARY_DOWNLOAD: ''
run: npm ci --prefer-offline --no-audit --no-fund
- name: Build desktop
run: npm run build:desktop
typecheck:
name: Typecheck
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Run typecheck
run: npm run typecheck
repo-drift:
name: Repo Drift Check
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Run repo drift check
run: npm run repo:drift-check
schema-sqlite:
name: Schema Check (SQLite)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Run sqlite schema parity
run: npm run test:schema:parity
- name: Run sqlite schema upgrade
run: npm run test:schema:upgrade
schema-mysql:
name: Schema Check (MySQL)
runs-on: ubuntu-latest
timeout-minutes: 20
services:
mysql:
image: mysql:8.4
env:
MYSQL_DATABASE: metapi
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -proot"
--health-interval=10s
--health-timeout=5s
--health-retries=10
env:
DB_PARITY_MYSQL_URL: mysql://root:root@127.0.0.1:3306/metapi
DB_PARITY_SQLITE: 'false'
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Run mysql schema parity
run: npm run test:schema:parity
- name: Run mysql schema upgrade
run: npm run test:schema:upgrade
- name: Run mysql runtime schema bootstrap
run: npm run test:schema:runtime
schema-postgres:
name: Schema Check (Postgres)
runs-on: ubuntu-latest
timeout-minutes: 20
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: metapi
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
ports:
- 5432:5432
options: >-
--health-cmd="pg_isready -U postgres -d metapi"
--health-interval=10s
--health-timeout=5s
--health-retries=10
env:
DB_PARITY_POSTGRES_URL: postgres://postgres:postgres@127.0.0.1:5432/metapi
DB_PARITY_SQLITE: 'false'
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Run postgres schema parity
run: npm run test:schema:parity
- name: Run postgres schema upgrade
run: npm run test:schema:upgrade
- name: Run postgres runtime schema bootstrap
run: npm run test:schema:runtime
docs-build:
name: Build Docs
if: github.event_name == 'pull_request' && needs.changes.outputs.docs == 'true'
needs: changes
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Build docs
run: npm run docs:build
audit:
name: Audit Production Dependencies
runs-on: ubuntu-latest
timeout-minutes: 10
# Keep PR audit informational so build/test/schema remain the merge gate.
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --prefer-offline --no-audit --no-fund
- name: Audit production dependencies
run: npm audit --omit=dev --audit-level=high
publish-docker-arch:
name: Publish Docker Image (${{ matrix.arch }})
if: github.event_name == 'push'
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
env:
DOCKERHUB_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/metapi
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
platform: linux/amd64
runner: ubuntu-latest
- arch: arm64
platform: linux/arm64
runner: ubuntu-24.04-arm
- arch: armv7
platform: linux/arm/v7
runner: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Ensure Docker Hub secrets are configured
run: |
if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
echo "Missing Docker Hub secrets: DOCKERHUB_USERNAME / DOCKERHUB_TOKEN"
exit 1
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Set up QEMU
if: matrix.arch == 'armv7'
uses: docker/setup-qemu-action@v4
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.DOCKERHUB_IMAGE }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=sha,format=short
- name: Add architecture suffix to tags
id: arch-tags
shell: bash
run: |
set -euo pipefail
rm -f tags.txt
while IFS= read -r tag; do
[ -n "$tag" ] || continue
echo "${tag}-${{ matrix.arch }}" >> tags.txt
done <<< "${{ steps.meta.outputs.tags }}"
{
echo 'tags<<EOF'
cat tags.txt
echo 'EOF'
} >> "$GITHUB_OUTPUT"
- name: Build and push ${{ matrix.platform }} image
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: ${{ matrix.platform }}
push: true
tags: ${{ steps.arch-tags.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=ci-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=ci-${{ matrix.arch }}
publish-docker:
name: Publish Docker Manifest
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs: publish-docker-arch
timeout-minutes: 15
env:
DOCKERHUB_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/metapi
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Ensure Docker Hub secrets are configured
run: |
if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
echo "Missing Docker Hub secrets: DOCKERHUB_USERNAME / DOCKERHUB_TOKEN"
exit 1
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.DOCKERHUB_IMAGE }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=sha,format=short
- name: Create multi-arch manifests
shell: bash
run: |
set -euo pipefail
while IFS= read -r tag; do
[ -n "$tag" ] || continue
docker buildx imagetools create \
--tag "$tag" \
"${tag}-amd64" \
"${tag}-arm64" \
"${tag}-armv7"
done <<< "${{ steps.meta.outputs.tags }}"