Compare commits
17 Commits
d5a44cc811
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
| 631235e16c | |||
| 55bfd808e9 | |||
| 7c86b1a87f | |||
| d26a9595d3 | |||
| d3b7db90e7 | |||
| 85547d7043 | |||
| ab7ff9a59b | |||
| 51de32d1de | |||
| 89e5143395 | |||
| d66f6b6be5 | |||
| 147a25614f | |||
| c243300670 | |||
| 32b4053ed3 | |||
| 5b3c599db6 | |||
| bfc30fac22 | |||
| 860ad35387 | |||
| e1612f63db |
+24
-23
@@ -11,31 +11,32 @@ env:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: [self-hosted, rent-deploy]
|
||||
runs-on: self-hosted
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Clone or update code
|
||||
run: |
|
||||
WORK_DIR="/tmp/rent-deploy"
|
||||
WORK_DIR="/workspace/rent-deploy"
|
||||
REPO_URL="https://gitea.pinzhuhui.com/xiaoquan/rent.git"
|
||||
|
||||
# 使用宿主机的 Gitea 端口
|
||||
GITEA_URL="http://host.docker.internal:10082/xiaoquan/rent.git"
|
||||
echo "准备代码..."
|
||||
|
||||
if [ -d "$WORK_DIR" ]; then
|
||||
echo "代码目录已存在,拉取最新代码..."
|
||||
if [ -d "$WORK_DIR/.git" ]; then
|
||||
echo "代码目录已存在,更新代码..."
|
||||
cd $WORK_DIR
|
||||
git fetch --all
|
||||
git reset --hard origin/${{ github.ref_name }}
|
||||
git fetch origin
|
||||
git checkout ${{ github.ref_name }}
|
||||
git pull origin ${{ github.ref_name }}
|
||||
else
|
||||
echo "首次克隆代码..."
|
||||
# 尝试多个可能的地址
|
||||
git clone $GITEA_URL $WORK_DIR || \
|
||||
git clone http://172.17.0.1:10082/xiaoquan/rent.git $WORK_DIR || \
|
||||
git clone http://localhost:10082/xiaoquan/rent.git $WORK_DIR
|
||||
|
||||
git clone $REPO_URL $WORK_DIR
|
||||
cd $WORK_DIR
|
||||
git checkout ${{ github.ref_name }}
|
||||
fi
|
||||
|
||||
echo "✅ 代码准备完成"
|
||||
echo "当前分支: $(git branch --show-current)"
|
||||
echo "最新提交: $(git log -1 --oneline)"
|
||||
|
||||
@@ -43,38 +44,38 @@ jobs:
|
||||
if: github.ref == 'refs/heads/test'
|
||||
run: |
|
||||
echo "部署到测试环境..."
|
||||
cd /tmp/rent-deploy/deploy/docker
|
||||
cd /workspace/rent-deploy/deploy/docker
|
||||
|
||||
export TEST_DB_PASSWORD="${{ secrets.TEST_DB_PASSWORD }}"
|
||||
export TEST_JWT_SECRET="${{ secrets.TEST_JWT_SECRET }}"
|
||||
export ENCRYPTION_KEY="${{ secrets.ENCRYPTION_KEY }}"
|
||||
|
||||
docker-compose -f docker-compose.test.yml down --remove-orphans
|
||||
docker-compose -f docker-compose.test.yml build --parallel
|
||||
docker-compose -f docker-compose.test.yml up -d
|
||||
docker compose -f docker-compose.test.yml down --remove-orphans
|
||||
docker compose -f docker-compose.test.yml build --parallel
|
||||
docker compose -f docker-compose.test.yml up -d
|
||||
docker image prune -f
|
||||
|
||||
echo "等待服务启动..."
|
||||
sleep 10
|
||||
docker-compose -f docker-compose.test.yml ps
|
||||
docker compose -f docker-compose.test.yml ps
|
||||
echo "✅ 测试环境部署完成"
|
||||
|
||||
- name: Deploy to production
|
||||
if: github.ref == 'refs/heads/prod'
|
||||
run: |
|
||||
echo "部署到生产环境..."
|
||||
cd /tmp/rent-deploy/deploy/docker
|
||||
cd /workspace/rent-deploy/deploy/docker
|
||||
|
||||
export PROD_DB_PASSWORD="${{ secrets.PROD_DB_PASSWORD }}"
|
||||
export PROD_JWT_SECRET="${{ secrets.PROD_JWT_SECRET }}"
|
||||
export ENCRYPTION_KEY="${{ secrets.ENCRYPTION_KEY }}"
|
||||
|
||||
docker-compose -f docker-compose.prod.yml down --remove-orphans
|
||||
docker-compose -f docker-compose.prod.yml build --parallel
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
docker compose -f docker-compose.prod.yml down --remove-orphans
|
||||
docker compose -f docker-compose.prod.yml build --parallel
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
docker image prune -f
|
||||
|
||||
echo "等待服务启动..."
|
||||
sleep 10
|
||||
docker-compose -f docker-compose.prod.yml ps
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
echo "✅ 生产环境部署完成"
|
||||
|
||||
@@ -49,3 +49,4 @@ pnpm dev:merchant
|
||||
# 启动平台管理后台
|
||||
pnpm dev:platform
|
||||
```
|
||||
# CI/CD Test 2026年06月10日 19:13:14
|
||||
|
||||
@@ -213,21 +213,21 @@ const Settlements: React.FC = () => {
|
||||
{detailModal.data.periodStart} ~ {detailModal.data.periodEnd}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="订单数量">{detailModal.data.orderCount} 笔</Descriptions.Item>
|
||||
<Descriptions.Item label="订单总额">{formatMoney(detailModal.data.orderAmount)}</Descriptions.Item>
|
||||
<Descriptions.Item label="订单总额">{formatMoney(detailModal.data.totalAmount)}</Descriptions.Item>
|
||||
<Descriptions.Item label="平台服务费">
|
||||
<span style={{ color: '#ff4d4f' }}>{formatMoney(detailModal.data.serviceFee)}</span>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="结算金额">
|
||||
<span style={{ fontWeight: 'bold', color: '#52c41a' }}>
|
||||
{formatMoney(detailModal.data.settlementAmount)}
|
||||
{formatMoney(detailModal.data.actualAmount)}
|
||||
</span>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="状态">
|
||||
<SettlementStatusTag status={detailModal.data.status} />
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="创建时间">{formatDateTime(detailModal.data.createdAt)}</Descriptions.Item>
|
||||
{detailModal.data.settledAt && (
|
||||
<Descriptions.Item label="结算时间">{formatDateTime(detailModal.data.settledAt)}</Descriptions.Item>
|
||||
{detailModal.data.paidAt && (
|
||||
<Descriptions.Item label="结算时间">{formatDateTime(detailModal.data.paidAt)}</Descriptions.Item>
|
||||
)}
|
||||
</Descriptions>
|
||||
)}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Card, Table, Tabs, Form, Input, Button, Space, Tag } from 'antd';
|
||||
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
import { getPlatformAccounts, getUserAccounts, getMerchantAccounts } from '@/api/finance';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { formatDateTime, formatMoney } from '@rent/shared-utils';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ interface FinancialOverview {
|
||||
merchantCount: number;
|
||||
todayIncome: number;
|
||||
todayExpense: number;
|
||||
monthIncome: number;
|
||||
monthExpense: number;
|
||||
}
|
||||
|
||||
interface Transaction {
|
||||
|
||||
@@ -40,6 +40,8 @@ const PlatformTransactions: React.FC = () => {
|
||||
setPage,
|
||||
setPageSize,
|
||||
refresh,
|
||||
reset,
|
||||
setParams,
|
||||
} = useTableData<PlatformTransaction>({
|
||||
fetchFn: async (params) => {
|
||||
const res = await getPlatformTransactions(params);
|
||||
@@ -67,12 +69,12 @@ const PlatformTransactions: React.FC = () => {
|
||||
params.endDate = values.dateRange[1].format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
refresh(params);
|
||||
setParams(params);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
refresh({ page: 1 });
|
||||
reset();
|
||||
};
|
||||
|
||||
const handleViewDetail = (record: PlatformTransaction) => {
|
||||
|
||||
@@ -50,6 +50,8 @@ const PlatformWithdrawals: React.FC = () => {
|
||||
setPage,
|
||||
setPageSize,
|
||||
refresh,
|
||||
reset,
|
||||
setParams,
|
||||
} = useTableData<PlatformWithdrawal>({
|
||||
fetchFn: async (params) => {
|
||||
const res = await getPlatformWithdrawals(params);
|
||||
@@ -66,12 +68,12 @@ const PlatformWithdrawals: React.FC = () => {
|
||||
|
||||
const handleSearch = () => {
|
||||
const values = form.getFieldsValue();
|
||||
refresh({ ...values, page: 1 });
|
||||
setParams({ ...values, page: 1 });
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
refresh({ page: 1 });
|
||||
reset();
|
||||
};
|
||||
|
||||
const handleViewDetail = (record: PlatformWithdrawal) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '@/common/guards/jwt-auth.guard';
|
||||
import { RolesGuard } from '@/common/guards/roles.guard';
|
||||
import { Roles } from '@/common/decorators/roles.decorator';
|
||||
import { AdminRole } from '@/entities/admin.entity';
|
||||
import { AdminRole } from '@/common/constants/admin.constant';
|
||||
import { SecretService } from '@/modules/shared/secret/secret.service';
|
||||
import { CreateSecretDto, UpdateSecretDto, QuerySecretDto } from './dto/secret.dto';
|
||||
|
||||
|
||||
@@ -56,10 +56,11 @@ export class SecretService {
|
||||
* 获取解密后的密钥值(供其他服务调用)
|
||||
*/
|
||||
async getDecryptedValue(secretKey: string, env: string = 'all'): Promise<string | null> {
|
||||
const envValue = env as 'all' | 'prod' | 'test';
|
||||
const secret = await this.secretRepo.findOne({
|
||||
where: [
|
||||
{ secretKey, env: 'all' },
|
||||
{ secretKey, env },
|
||||
{ secretKey, env: envValue },
|
||||
],
|
||||
});
|
||||
if (!secret) return null;
|
||||
@@ -71,8 +72,9 @@ export class SecretService {
|
||||
*/
|
||||
async create(data: { secretKey: string; secretValue: string; description?: string; groupName?: string; env?: string }) {
|
||||
// 检查是否已存在
|
||||
const envValue = (data.env as 'all' | 'prod' | 'test') || 'all';
|
||||
const existing = await this.secretRepo.findOne({
|
||||
where: { secretKey: data.secretKey, env: (data.env as any) || 'all' },
|
||||
where: { secretKey: data.secretKey, env: envValue },
|
||||
});
|
||||
if (existing) throw new ConflictException(`密钥 ${data.secretKey} 已存在`);
|
||||
|
||||
@@ -81,7 +83,7 @@ export class SecretService {
|
||||
secretValue: encrypt(data.secretValue),
|
||||
description: data.description,
|
||||
groupName: data.groupName || 'default',
|
||||
env: data.env || 'all',
|
||||
env: envValue,
|
||||
});
|
||||
return this.secretRepo.save(secret);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# CI/CD 部署配置指南
|
||||
|
||||
由于 Gitea Runner 运行在 Docker 容器中,无法直接访问宿主机文件系统和网络,我们采用以下方案:
|
||||
|
||||
## 📋 一次性配置步骤
|
||||
|
||||
### 1. 在服务器上手动克隆代码
|
||||
|
||||
SSH 登录服务器后执行:
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd /www/dk_project/dk_app
|
||||
|
||||
# 克隆代码(使用服务器本地 Gitea 端口)
|
||||
git clone http://127.0.0.1:10082/xiaoquan/rent.git
|
||||
|
||||
# 验证
|
||||
cd rent
|
||||
ls -la
|
||||
```
|
||||
|
||||
### 2. 配置 Git 凭证(避免每次输入密码)
|
||||
|
||||
```bash
|
||||
cd /www/dk_project/dk_app/rent
|
||||
|
||||
# 方法1:使用 Gitea Token
|
||||
git config credential.helper store
|
||||
git pull # 第一次会要求输入用户名和 token
|
||||
|
||||
# 方法2:使用 SSH(推荐)
|
||||
# 在 Gitea 中添加服务器的 SSH 公钥
|
||||
ssh-keygen -t rsa -b 4096
|
||||
cat ~/.ssh/id_rsa.pub
|
||||
# 复制公钥,在 Gitea 个人设置 -> SSH密钥 中添加
|
||||
|
||||
# 修改 remote 为 SSH
|
||||
git remote set-url origin git@127.0.0.1:xiaoquan/rent.git
|
||||
```
|
||||
|
||||
### 3. 验证自动拉取
|
||||
|
||||
```bash
|
||||
cd /www/dk_project/dk_app/rent
|
||||
git pull origin test
|
||||
```
|
||||
|
||||
应该能够无密码拉取成功。
|
||||
|
||||
---
|
||||
|
||||
## 🔄 CI/CD 工作流程
|
||||
|
||||
配置完成后,工作流程为:
|
||||
|
||||
1. **开发** → 修改代码
|
||||
2. **git push origin test** → 推送到 Gitea
|
||||
3. **CI/CD 自动触发** →
|
||||
- SSH 到服务器
|
||||
- git pull 拉取最新代码
|
||||
- docker-compose build 构建
|
||||
- docker-compose up 部署
|
||||
|
||||
---
|
||||
|
||||
## 🚀 使用方式
|
||||
|
||||
### 推送到测试环境
|
||||
```bash
|
||||
git push origin test
|
||||
```
|
||||
|
||||
### 推送到生产环境
|
||||
```bash
|
||||
git push origin prod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **首次配置**:必须在服务器上手动克隆一次代码
|
||||
2. **Git 凭证**:配置后才能自动 git pull
|
||||
3. **路径固定**:代码路径为 `/www/dk_project/dk_app/rent`
|
||||
4. **权限**:确保 Docker 有权限访问代码目录
|
||||
|
||||
---
|
||||
|
||||
## 🔧 故障排查
|
||||
|
||||
### 问题:CI/CD 提示找不到代码目录
|
||||
|
||||
**解决**:检查服务器上是否已克隆代码
|
||||
```bash
|
||||
ls -la /www/dk_project/dk_app/rent
|
||||
```
|
||||
|
||||
### 问题:git pull 需要密码
|
||||
|
||||
**解决**:配置 Git 凭证(见上面步骤2)
|
||||
|
||||
### 问题:Docker 构建失败
|
||||
|
||||
**解决**:检查 Dockerfile 和依赖
|
||||
```bash
|
||||
cd /www/dk_project/dk_app/rent/deploy/docker
|
||||
docker-compose -f docker-compose.test.yml build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**维护者**:开发团队
|
||||
**最后更新**:2026-06-10
|
||||
@@ -4,7 +4,7 @@ WORKDIR /app
|
||||
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
|
||||
COPY packages/ ./packages/
|
||||
COPY apps/merchant-admin/ ./apps/merchant-admin/
|
||||
RUN npm install -g pnpm && pnpm install --frozen-lockfile
|
||||
RUN npm install -g pnpm@8 && pnpm install --frozen-lockfile
|
||||
RUN pnpm --filter @rent/shared-types build
|
||||
RUN pnpm --filter @rent/shared-utils build
|
||||
RUN cd apps/merchant-admin && pnpm run build
|
||||
|
||||
@@ -4,7 +4,7 @@ WORKDIR /app
|
||||
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
|
||||
COPY packages/ ./packages/
|
||||
COPY apps/platform-admin/ ./apps/platform-admin/
|
||||
RUN npm install -g pnpm && pnpm install --frozen-lockfile
|
||||
RUN npm install -g pnpm@8 && pnpm install --frozen-lockfile
|
||||
RUN pnpm --filter @rent/shared-types build
|
||||
RUN pnpm --filter @rent/shared-utils build
|
||||
RUN cd apps/platform-admin && pnpm run build
|
||||
|
||||
@@ -4,7 +4,7 @@ WORKDIR /app
|
||||
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
|
||||
COPY packages/ ./packages/
|
||||
COPY apps/server/ ./apps/server/
|
||||
RUN npm install -g pnpm && pnpm install --frozen-lockfile
|
||||
RUN npm install -g pnpm@8 && pnpm install --frozen-lockfile
|
||||
RUN cd apps/server && pnpm run build
|
||||
|
||||
FROM node:18-alpine AS server
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# 官网 - Next.js 静态导出
|
||||
FROM node:18-alpine AS website-builder
|
||||
FROM node:20-alpine AS website-builder
|
||||
WORKDIR /app
|
||||
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
|
||||
COPY packages/ ./packages/
|
||||
COPY apps/official-website/ ./apps/official-website/
|
||||
RUN npm install -g pnpm && pnpm install --frozen-lockfile
|
||||
RUN npm install -g pnpm@8 && pnpm install --frozen-lockfile
|
||||
RUN cd apps/official-website && pnpm run build
|
||||
|
||||
FROM nginx:alpine AS website
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
# Webhook 部署方案
|
||||
|
||||
由于 Gitea Runner 容器环境限制,采用更简单可靠的 Webhook 方式触发部署。
|
||||
|
||||
## 🚀 配置步骤
|
||||
|
||||
### 第一步:在服务器上配置部署脚本
|
||||
|
||||
SSH 登录服务器后执行:
|
||||
|
||||
```bash
|
||||
# 1. 克隆代码(如果还没有)
|
||||
cd /www/dk_project/dk_app
|
||||
git clone https://gitea.pinzhuhui.com/xiaoquan/rent.git
|
||||
cd rent
|
||||
|
||||
# 2. 配置 Git 凭证
|
||||
git config credential.helper store
|
||||
git pull origin test # 输入用户名和 Gitea Token
|
||||
|
||||
# 3. 赋予部署脚本执行权限
|
||||
chmod +x deploy/scripts/deploy.sh
|
||||
|
||||
# 4. 创建环境变量文件
|
||||
cat > /root/.env.test << 'EOF'
|
||||
export TEST_DB_PASSWORD="your_password"
|
||||
export TEST_JWT_SECRET="your_jwt_secret"
|
||||
export ENCRYPTION_KEY="your_encryption_key"
|
||||
EOF
|
||||
|
||||
cat > /root/.env.prod << 'EOF'
|
||||
export PROD_DB_PASSWORD="your_password"
|
||||
export PROD_JWT_SECRET="your_jwt_secret"
|
||||
export ENCRYPTION_KEY="your_encryption_key"
|
||||
EOF
|
||||
|
||||
# 5. 测试部署脚本
|
||||
/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh test
|
||||
```
|
||||
|
||||
### 第二步:配置 Webhook 服务
|
||||
|
||||
#### 方法1:使用 webhook (推荐)
|
||||
|
||||
```bash
|
||||
# 安装 webhook
|
||||
cd /root
|
||||
wget https://github.com/adnanh/webhook/releases/download/2.8.1/webhook-linux-amd64.tar.gz
|
||||
tar -xzf webhook-linux-amd64.tar.gz
|
||||
mv webhook-linux-amd64/webhook /usr/local/bin/
|
||||
chmod +x /usr/local/bin/webhook
|
||||
|
||||
# 创建 webhook 配置
|
||||
mkdir -p /root/webhooks
|
||||
cat > /root/webhooks/hooks.json << 'EOF'
|
||||
[
|
||||
{
|
||||
"id": "rent-deploy-test",
|
||||
"execute-command": "/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh",
|
||||
"command-working-directory": "/www/dk_project/dk_app/rent",
|
||||
"pass-arguments-to-command": [
|
||||
{
|
||||
"source": "string",
|
||||
"name": "test"
|
||||
}
|
||||
],
|
||||
"trigger-rule": {
|
||||
"and": [
|
||||
{
|
||||
"match": {
|
||||
"type": "payload-hash-sha256",
|
||||
"secret": "your-webhook-secret",
|
||||
"parameter": {
|
||||
"source": "header",
|
||||
"name": "X-Gitea-Signature"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"type": "value",
|
||||
"value": "refs/heads/test",
|
||||
"parameter": {
|
||||
"source": "payload",
|
||||
"name": "ref"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rent-deploy-prod",
|
||||
"execute-command": "/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh",
|
||||
"command-working-directory": "/www/dk_project/dk_app/rent",
|
||||
"pass-arguments-to-command": [
|
||||
{
|
||||
"source": "string",
|
||||
"name": "prod"
|
||||
}
|
||||
],
|
||||
"trigger-rule": {
|
||||
"and": [
|
||||
{
|
||||
"match": {
|
||||
"type": "payload-hash-sha256",
|
||||
"secret": "your-webhook-secret",
|
||||
"parameter": {
|
||||
"source": "header",
|
||||
"name": "X-Gitea-Signature"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"type": "value",
|
||||
"value": "refs/heads/prod",
|
||||
"parameter": {
|
||||
"source": "payload",
|
||||
"name": "ref"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
EOF
|
||||
|
||||
# 启动 webhook 服务
|
||||
nohup webhook -hooks /root/webhooks/hooks.json -verbose -port 9000 > /root/webhooks/webhook.log 2>&1 &
|
||||
|
||||
# 设置开机自启
|
||||
cat > /etc/systemd/system/webhook.service << 'EOF'
|
||||
[Unit]
|
||||
Description=Webhook Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/webhook -hooks /root/webhooks/hooks.json -verbose -port 9000
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable webhook
|
||||
systemctl start webhook
|
||||
```
|
||||
|
||||
#### 方法2:使用简单的 PHP 脚本(如果有宝塔面板)
|
||||
|
||||
在宝塔面板创建一个站点,添加 `webhook.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
// webhook.php
|
||||
$secret = 'your-webhook-secret';
|
||||
$branch = $_POST['ref'] ?? '';
|
||||
|
||||
// 验证签名
|
||||
$signature = $_SERVER['HTTP_X_GITEA_SIGNATURE'] ?? '';
|
||||
$payload = file_get_contents('php://input');
|
||||
$expected = hash_hmac('sha256', $payload, $secret);
|
||||
|
||||
if (!hash_equals($expected, $signature)) {
|
||||
http_response_code(403);
|
||||
die('Invalid signature');
|
||||
}
|
||||
|
||||
// 执行部署
|
||||
if ($branch === 'refs/heads/test') {
|
||||
$output = shell_exec('/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh test 2>&1');
|
||||
} elseif ($branch === 'refs/heads/prod') {
|
||||
$output = shell_exec('/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh prod 2>&1');
|
||||
} else {
|
||||
http_response_code(400);
|
||||
die('Unknown branch');
|
||||
}
|
||||
|
||||
echo $output;
|
||||
?>
|
||||
```
|
||||
|
||||
### 第三步:在 Gitea 中配置 Webhook
|
||||
|
||||
1. 登录 Gitea
|
||||
2. 进入仓库 → Settings → Webhooks
|
||||
3. 点击 "添加 Webhook" → "Gitea"
|
||||
4. 配置:
|
||||
- **目标 URL**:
|
||||
- webhook 方式:`http://your-server:9000/hooks/rent-deploy-test`
|
||||
- PHP 方式:`http://your-server/webhook.php`
|
||||
- **HTTP 方法**: POST
|
||||
- **POST Content Type**: application/json
|
||||
- **密钥**: `your-webhook-secret`(与上面配置的一致)
|
||||
- **触发条件**: 勾选 "推送事件"
|
||||
- **分支过滤**: `test` 或 `prod`
|
||||
5. 点击 "添加 Webhook"
|
||||
6. 点击 "测试推送" 验证
|
||||
|
||||
---
|
||||
|
||||
## 🔄 工作流程
|
||||
|
||||
```
|
||||
1. git push origin test
|
||||
↓
|
||||
2. Gitea 触发 Webhook
|
||||
↓
|
||||
3. Webhook 服务调用 deploy.sh
|
||||
↓
|
||||
4. 脚本自动:
|
||||
- git pull 更新代码
|
||||
- docker-compose build 构建
|
||||
- docker-compose up 部署
|
||||
↓
|
||||
5. 完成!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 查看日志
|
||||
|
||||
### webhook 方式
|
||||
```bash
|
||||
tail -f /root/webhooks/webhook.log
|
||||
```
|
||||
|
||||
### 手动测试
|
||||
```bash
|
||||
/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 优点
|
||||
|
||||
- ✅ 不依赖 Runner 容器
|
||||
- ✅ 直接在宿主机执行
|
||||
- ✅ 简单可靠
|
||||
- ✅ 容易调试
|
||||
|
||||
---
|
||||
|
||||
**维护者**:开发团队
|
||||
**最后更新**:2026-06-10
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Gitea Webhook 部署脚本
|
||||
# 用法:./deploy.sh <branch>
|
||||
|
||||
set -e
|
||||
|
||||
BRANCH=${1:-test}
|
||||
PROJECT_DIR="/www/dk_project/dk_app/rent"
|
||||
REPO_URL="https://gitea.pinzhuhui.com/xiaoquan/rent.git"
|
||||
|
||||
echo "========================================="
|
||||
echo "开始部署 - 分支: $BRANCH"
|
||||
echo "========================================="
|
||||
|
||||
# 1. 初始化或更新代码
|
||||
if [ ! -d "$PROJECT_DIR" ]; then
|
||||
echo "首次克隆代码..."
|
||||
cd /www/dk_project/dk_app
|
||||
git clone $REPO_URL
|
||||
cd $PROJECT_DIR
|
||||
else
|
||||
echo "更新代码..."
|
||||
cd $PROJECT_DIR
|
||||
git fetch origin
|
||||
git checkout $BRANCH
|
||||
git pull origin $BRANCH
|
||||
fi
|
||||
|
||||
echo "✅ 代码更新完成"
|
||||
echo "当前分支: $(git branch --show-current)"
|
||||
echo "最新提交: $(git log -1 --oneline)"
|
||||
|
||||
# 2. 部署
|
||||
cd $PROJECT_DIR/deploy/docker
|
||||
|
||||
if [ "$BRANCH" == "test" ]; then
|
||||
echo "========================================="
|
||||
echo "部署到测试环境"
|
||||
echo "========================================="
|
||||
|
||||
# 从环境变量或密钥文件读取
|
||||
source /root/.env.test 2>/dev/null || true
|
||||
|
||||
docker-compose -f docker-compose.test.yml down --remove-orphans
|
||||
docker-compose -f docker-compose.test.yml build --parallel
|
||||
docker-compose -f docker-compose.test.yml up -d
|
||||
docker image prune -f
|
||||
|
||||
echo "等待服务启动..."
|
||||
sleep 10
|
||||
docker-compose -f docker-compose.test.yml ps
|
||||
|
||||
echo "✅ 测试环境部署完成"
|
||||
|
||||
elif [ "$BRANCH" == "prod" ]; then
|
||||
echo "========================================="
|
||||
echo "部署到生产环境"
|
||||
echo "========================================="
|
||||
|
||||
# 从环境变量或密钥文件读取
|
||||
source /root/.env.prod 2>/dev/null || true
|
||||
|
||||
docker-compose -f docker-compose.prod.yml down --remove-orphans
|
||||
docker-compose -f docker-compose.prod.yml build --parallel
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
docker image prune -f
|
||||
|
||||
echo "等待服务启动..."
|
||||
sleep 10
|
||||
docker-compose -f docker-compose.prod.yml ps
|
||||
|
||||
echo "✅ 生产环境部署完成"
|
||||
else
|
||||
echo "❌ 未知分支: $BRANCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "========================================="
|
||||
echo "部署完成!"
|
||||
echo "========================================="
|
||||
Reference in New Issue
Block a user