chore(build): add build script
Some checks are pending
Build and Publish / manifest (push) Blocked by required conditions
Build and Publish / ci (push) Waiting to run
Build and Publish / docker (app) (push) Blocked by required conditions
Build and Publish / docker (email-worker) (push) Blocked by required conditions
Build and Publish / docker (git-hook) (push) Blocked by required conditions
Build and Publish / docker (gitserver) (push) Blocked by required conditions
Build and Publish / docker (migrate) (push) Blocked by required conditions
Build and Publish / docker (operator) (push) Blocked by required conditions
Build and Publish / docker-arm64 (app) (push) Blocked by required conditions
Build and Publish / docker-arm64 (email-worker) (push) Blocked by required conditions
Build and Publish / docker-arm64 (git-hook) (push) Blocked by required conditions
Build and Publish / docker-arm64 (gitserver) (push) Blocked by required conditions
Build and Publish / docker-arm64 (migrate) (push) Blocked by required conditions

This commit is contained in:
ZhenYi 2026-04-14 20:22:06 +08:00
parent 07ae21af81
commit c330c23cef
6 changed files with 511 additions and 0 deletions

109
.gitea/README.md Normal file
View File

@ -0,0 +1,109 @@
# Gitea Actions 配置
## 目录结构
```
.gitea/
└── workflows/
└── build.yaml # 构建流水线
```
## 快速开始
### 1. 启用 Gitea Actions
在 Gitea 管理面板中启用 Actions
```ini
[actions]
ENABLED = true
```
### 2. 注册 Runner
在 Gitea 仓库设置中创建 Runner获取 Token 后配置:
```bash
# Docker 部署
docker run -d \
--name act-runner \
-e GITEA_INSTANCE_URL=https://git.example.com \
-e GITEA_RUNNER_TOKEN=<your-token> \
-v /var/run/docker.sock:/var/run/docker.sock \
-v act-runner-data:/data \
gitea/act-runner:latest
# 或使用 Helm (见下方)
```
### 3. 添加 Secrets
在 Gitea 仓库设置中添加:
| Secret | 说明 |
|--------|------|
| `HARBOR_USERNAME` | Harbor 用户名 |
| `HARBOR_PASSWORD` | Harbor 密码 |
## Helm 部署 Act Runner
```bash
# values override
cat > act-runner-values.yaml << 'EOF'
actRunner:
enabled: true
replicaCount: 2
capacity: 2
labels:
- gitea
- docker
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
EOF
helm upgrade --install act-runner ./deploy \
-f act-runner-values.yaml \
-n c-----code \
--create-namespace
```
## Workflow 说明
### build.yaml
| Stage | 说明 |
|-------|------|
| `ci` | 格式化检查、Clippy lint、单元测试 |
| `docker` | x86_64 镜像构建并推送到 Harbor |
| `docker-arm64` | ARM64 镜像构建 (需 ARM64 runner) |
| `manifest` | 多架构镜像清单合并 |
### Runner 标签
| Label | 说明 |
|-------|------|
| `gitea` | 默认 runner |
| `docker` | 支持 Docker-in-Docker |
| `arm64` | ARM64 架构 runner |
### 触发条件
- Push 到 `main` 分支
- Pull Request 到 `main` 分支
## 本地测试
使用 [nektos/act](https://github.com/nektos/act) 本地运行:
```bash
# 安装
curl -sSL https://raw.githubusercontent.com/nektos/act/master/install.sh | sh
# 运行 workflow
act -W .gitea/workflows/build.yaml
```

View File

@ -0,0 +1,42 @@
# User overrides for Helm deployment
# Copy to values.user.yaml and customize
# PostgreSQL
database:
existingSecret: c-----code-secrets
secretKeys:
url: APP_DATABASE_URL
# Redis
redis:
existingSecret: c-----code-secrets
secretKeys:
url: APP_REDIS_URL
# NATS (if using hook pool)
nats:
enabled: false
url: nats://nats:4222
# Qdrant (if using AI embeddings)
qdrant:
enabled: false
url: http://qdrant:6333
# App configuration
app:
replicaCount: 3
ingress:
enabled: true
hosts:
- host: git.example.com
# Gitserver persistence
gitserver:
persistence:
size: 100Gi
storageClass: fast-ssd
# Act Runner
actRunner:
enabled: false

107
scripts/README.md Normal file
View File

@ -0,0 +1,107 @@
# 构建脚本
## 一键构建脚本
### build.js - 构建镜像
```bash
# 构建所有镜像
node scripts/build.js
# 构建指定服务
node scripts/build.js app gitserver
# 指定 tag
TAG=v1.0.0 node scripts/build.js
# 指定架构
TARGET=aarch64-unknown-linux-gnu node scripts/build.js
```
**环境变量:**
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `REGISTRY` | `harbor.gitdata.me/gta_team` | 镜像仓库 |
| `TAG` | `latest` | 镜像标签 |
| `TARGET` | `x86_64-unknown-linux-gnu` | Rust 交叉编译目标 |
---
### push.js - 推送镜像
```bash
# 推送所有镜像
HARBOR_USERNAME=user HARBOR_PASSWORD=pass node scripts/push.js
# 推送指定服务
HARBOR_USERNAME=user HARBOR_PASSWORD=pass TAG=sha-abc123 node scripts/push.js app
```
**环境变量:**
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `REGISTRY` | `harbor.gitdata.me/gta_team` | 镜像仓库 |
| `TAG` | `latest` 或 Git SHA | 镜像标签 |
| `HARBOR_USERNAME` | - | **必填** 仓库用户名 |
| `HARBOR_PASSWORD` | - | **必填** 仓库密码 |
---
### deploy.js - 部署到 Kubernetes
```bash
# 部署最新镜像
node scripts/deploy.js
# 干跑模式(不实际部署)
node scripts/deploy.js --dry-run
# 部署并运行数据库迁移
node scripts/deploy.js --migrate
# 指定 tag
TAG=sha-abc123 node scripts/deploy.js
# 指定命名空间
NAMESPACE=staging node scripts/deploy.js
```
**环境变量:**
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `REGISTRY` | `harbor.gitdata.me/gta_team` | 镜像仓库 |
| `TAG` | `latest` 或 Git SHA | 镜像标签 |
| `NAMESPACE` | `c-----code` | K8s 命名空间 |
| `RELEASE` | `c-----code` | Helm Release 名称 |
| `KUBECONFIG` | `~/.kube/config` | Kubeconfig 路径 |
---
## 完整 CI/CD 流程
```bash
# 1. 构建
node scripts/build.js
# 2. 推送
HARBOR_USERNAME=user HARBOR_PASSWORD=pass node scripts/push.js
# 3. 部署
node scripts/deploy.js --migrate
```
## 本地开发
```bash
# 本地构建测试
node scripts/build.js app
# 使用本地 tag
TAG=dev node scripts/build.js
# 部署到测试环境
NAMESPACE=test node scripts/deploy.js
```

69
scripts/build.js Normal file
View File

@ -0,0 +1,69 @@
#!/usr/bin/env node
/**
* Build Docker images for C-----code
*
* Usage:
* node scripts/build.js # Build all images
* node scripts/build.js app # Build specific service
* node scripts/build.js app gitserver
*
* Environment:
* REGISTRY - Docker registry (default: harbor.gitdata.me/gta_team)
* TAG - Image tag (default: latest)
* TARGET - Rust build target (default: x86_64-unknown-linux-gnu)
*/
const { execSync } = require('child_process');
const path = require('path');
const REGISTRY = process.env.REGISTRY || 'harbor.gitdata.me/gta_team';
const TAG = process.env.TAG || 'latest';
const BUILD_TARGET = process.env.TARGET || 'x86_64-unknown-linux-gnu';
const SERVICES = ['app', 'gitserver', 'email-worker', 'git-hook', 'migrate', 'operator'];
const args = process.argv.slice(2);
const targets = args.length > 0 ? args : SERVICES;
console.log(`\n=== Build Configuration ===`);
console.log(`Registry: ${REGISTRY}`);
console.log(`Tag: ${TAG}`);
console.log(`Target: ${BUILD_TARGET}`);
console.log(`Services: ${targets.join(', ')}\n`);
for (const service of targets) {
if (!SERVICES.includes(service)) {
console.error(`Unknown service: ${service}`);
console.error(`Available: ${SERVICES.join(', ')}`);
process.exit(1);
}
const dockerfile = path.join(__dirname, '..', 'docker', `${service}.Dockerfile`);
const image = `${REGISTRY}/${service}:${TAG}`;
console.log(`\n==> Building ${image}`);
console.log(` Dockerfile: ${dockerfile}`);
try {
execSync(
`docker buildx build ` +
`--build-arg BUILD_TARGET=${BUILD_TARGET} ` +
`-f "${dockerfile}" ` +
`-t "${image}" ` +
`--load ` +
`--progress=plain ` +
`.`,
{ stdio: 'inherit', cwd: path.join(__dirname, '..') }
);
console.log(` [OK] ${image}`);
} catch (error) {
console.error(` [FAIL] ${service}`);
process.exit(1);
}
}
console.log(`\n=== Build Complete ===`);
for (const service of targets) {
console.log(` ${REGISTRY}/${service}:${TAG}`);
}
console.log('');

114
scripts/deploy.js Normal file
View File

@ -0,0 +1,114 @@
#!/usr/bin/env node
/**
* Deploy C-----code via Helm
*
* Usage:
* node scripts/deploy.js # Deploy with defaults
* node scripts/deploy.js --dry-run # Dry run only
* node scripts/deploy.js --migrate # Run migrations first
*
* Environment:
* REGISTRY - Docker registry (default: harbor.gitdata.me/gta_team)
* TAG - Image tag (default: latest)
* NAMESPACE - Kubernetes namespace (default: c-----code)
* RELEASE - Helm release name (default: c-----code)
* KUBECONFIG - Path to kubeconfig (default: ~/.kube/config)
*/
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const REGISTRY = process.env.REGISTRY || 'harbor.gitdata.me/gta_team';
const TAG = process.env.TAG || process.env.GITHUB_SHA?.substring(0, 8) || 'latest';
const NAMESPACE = process.env.NAMESPACE || 'c-----code';
const RELEASE = process.env.RELEASE || 'c-----code';
const CHART_PATH = path.join(__dirname, '..', 'deploy');
const KUBECONFIG = process.env.KUBECONFIG || path.join(process.env.HOME || process.env.USERPROFILE, '.kube', 'config');
const args = process.argv.slice(2);
const isDryRun = args.includes('--dry-run');
const runMigrate = args.includes('--migrate');
const SERVICES = ['app', 'gitserver', 'email-worker', 'git-hook', 'operator'];
// Validate kubeconfig
if (!fs.existsSync(KUBECONFIG)) {
console.error(`Error: kubeconfig not found at ${KUBECONFIG}`);
console.error('Set KUBECONFIG environment variable or ensure ~/.kube/config exists');
process.exit(1);
}
console.log(`\n=== Deploy Configuration ===`);
console.log(`Registry: ${REGISTRY}`);
console.log(`Tag: ${TAG}`);
console.log(`Namespace: ${NAMESPACE}`);
console.log(`Release: ${RELEASE}`);
console.log(`Dry Run: ${isDryRun}`);
console.log(`Run Migrate: ${runMigrate}`);
console.log('');
// Build helm values override
const valuesFile = path.join(CHART_PATH, 'values.yaml');
const userValuesFile = path.join(CHART_PATH, 'values.user.yaml');
const setValues = [
`image.registry=${REGISTRY}`,
`app.image.tag=${TAG}`,
`gitserver.image.tag=${TAG}`,
`emailWorker.image.tag=${TAG}`,
`gitHook.image.tag=${TAG}`,
`operator.image.tag=${TAG}`,
];
if (runMigrate) {
setValues.push('migrate.enabled=true');
}
const helmArgs = [
'upgrade', '--install', RELEASE, CHART_PATH,
'--namespace', NAMESPACE,
'--create-namespace',
'-f', valuesFile,
...(fs.existsSync(userValuesFile) ? ['-f', userValuesFile] : []),
...setValues.flatMap(v => ['--set', v]),
'--wait',
'--timeout', '5m',
];
if (isDryRun) {
helmArgs.push('--dry-run');
console.log('==> Dry run mode - no changes will be made\n');
}
// Helm upgrade
console.log(`==> Running helm upgrade`);
console.log(` Command: helm ${helmArgs.join(' ')}\n`);
try {
execSync(`helm ${helmArgs.join(' ')}`, { stdio: 'inherit', env: { ...process.env, KUBECONFIG } });
console.log(`\n[OK] Deployment ${isDryRun ? '(dry-run) ' : ''}complete`);
} catch (error) {
console.error('\n[FAIL] Deployment failed');
process.exit(1);
}
// Rollout status
if (!isDryRun) {
console.log('\n==> Checking rollout status');
for (const service of SERVICES) {
const deploymentName = `${RELEASE}-${service}`;
console.log(` Checking ${deploymentName}...`);
try {
execSync(
`kubectl rollout status deployment/${deploymentName} -n ${NAMESPACE} --timeout=120s`,
{ stdio: 'pipe', env: { ...process.env, KUBECONFIG } }
);
console.log(` [OK] ${deploymentName}`);
} catch (error) {
console.error(` [WARN] ${deploymentName} rollout timeout or failed`);
}
}
}
console.log('\n=== Deploy Complete ===\n');

70
scripts/push.js Normal file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env node
/**
* Push Docker images to registry
*
* Usage:
* node scripts/push.js # Push all images
* node scripts/push.js app # Push specific service
*
* Environment:
* REGISTRY - Docker registry (default: harbor.gitdata.me/gta_team)
* TAG - Image tag (default: latest)
* DOCKER_USER - Registry username
* DOCKER_PASS - Registry password
*/
const { execSync } = require('child_process');
const REGISTRY = process.env.REGISTRY || 'harbor.gitdata.me/gta_team';
const TAG = process.env.TAG || process.env.GITHUB_SHA?.substring(0, 8) || 'latest';
const DOCKER_USER = process.env.DOCKER_USER || process.env.HARBOR_USERNAME;
const DOCKER_PASS = process.env.DOCKER_PASS || process.env.HARBOR_PASSWORD;
const SERVICES = ['app', 'gitserver', 'email-worker', 'git-hook', 'migrate', 'operator'];
const args = process.argv.slice(2);
const targets = args.length > 0 ? args : SERVICES;
if (!DOCKER_USER || !DOCKER_PASS) {
console.error('Error: DOCKER_USER and DOCKER_PASS environment variables are required');
console.error('Set HARBOR_USERNAME and HARBOR_PASSWORD as alternative');
process.exit(1);
}
console.log(`\n=== Push Configuration ===`);
console.log(`Registry: ${REGISTRY}`);
console.log(`Tag: ${TAG}`);
console.log(`Services: ${targets.join(', ')}\n`);
// Login
console.log(`==> Logging in to ${REGISTRY}`);
try {
execSync(`docker login ${REGISTRY} -u "${DOCKER_USER}" -p "${DOCKER_PASS}"`, {
stdio: 'inherit'
});
} catch (error) {
console.error('Login failed');
process.exit(1);
}
for (const service of targets) {
if (!SERVICES.includes(service)) {
console.error(`Unknown service: ${service}`);
process.exit(1);
}
const image = `${REGISTRY}/${service}:${TAG}`;
console.log(`\n==> Pushing ${image}`);
try {
execSync(`docker push "${image}" --all-tags`, { stdio: 'inherit' });
console.log(` [OK] ${image}`);
} catch (error) {
console.error(` [FAIL] ${service}`);
process.exit(1);
}
}
console.log(`\n=== Push Complete ===`);
console.log('');