feat: 版本号管理、构建日志持久化、App_Store 构建号自动递增
- 新增版本号配置 API(GET/PUT /api/config/versions)和前端管理界面
- 构建日志收集与保存:任务完成/取消/失败时写入 build.log
- 新增任务日志查询接口 GET /api/tasks/{task_id}/log
- App_Store 打包时自动递增 Build_Ver 第四位版本号
- 从 Provisioning Profile 自动提取 TEAM_ID 和 PROVISIONING_NAME
- 前端完成后支持查看完整构建日志
This commit is contained in:
@@ -173,6 +173,27 @@ const fetchTaskDetail = async (taskId) => {
|
||||
}
|
||||
}
|
||||
|
||||
const fetchBuildLog = async (taskId) => {
|
||||
try {
|
||||
const res = await fetch(`/api/tasks/${taskId}/log`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
if (data.log) {
|
||||
const lines = data.log.split('\n').filter(Boolean)
|
||||
logs.value = lines.map(line => {
|
||||
const m = line.match(/^\[(.+?)\]\s+\[(.+?)\]\s+(.*)$/)
|
||||
if (m) {
|
||||
return { timestamp: m[1], level: m[2].toLowerCase(), message: m[3] }
|
||||
}
|
||||
return { timestamp: '', level: 'info', message: line }
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
const connectWs = (taskId) => {
|
||||
if (activeWs) {
|
||||
activeWs.close()
|
||||
@@ -215,6 +236,12 @@ const connectWs = (taskId) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
ws.onclose = () => {
|
||||
// WebSocket 关闭后,尝试从服务器获取完整日志
|
||||
if (logs.value.length === 0) {
|
||||
fetchBuildLog(taskId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => currentTaskId.value, (newId) => {
|
||||
@@ -248,7 +275,10 @@ onMounted(async () => {
|
||||
// 已完成/失败的任务,直接显示结果
|
||||
if (task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') {
|
||||
completedTask.value = task
|
||||
logs.value = [{ timestamp: task.completed_at || task.created_at, level: task.status === 'failed' ? 'error' : 'step', message: task.status === 'failed' ? `打包失败: ${task.error_message || ''}` : '打包完成' }]
|
||||
await fetchBuildLog(queryTaskId)
|
||||
if (!logs.value.length) {
|
||||
logs.value = [{ timestamp: task.completed_at || task.created_at, level: task.status === 'failed' ? 'error' : 'step', message: task.status === 'failed' ? `打包失败: ${task.error_message || ''}` : '打包完成' }]
|
||||
}
|
||||
}
|
||||
// 设置 currentTaskId(触发 watch → connectWs,用 flag 跳过)
|
||||
skipNextConnect = true
|
||||
|
||||
@@ -230,6 +230,20 @@
|
||||
<div v-if="tab === 'build'">
|
||||
<h2>打包设置</h2>
|
||||
<div style="max-width: 500px;">
|
||||
<h4 class="section-title">应用版本号</h4>
|
||||
<div class="form-group">
|
||||
<label>App_Ver(应用版本)</label>
|
||||
<input type="text" v-model="versions.app_ver" placeholder="2.180.0">
|
||||
<div style="font-size: 12px; color: #999; margin-top: 4px;">格式:主版本.次版本.修订号</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Build_Ver(构建版本)</label>
|
||||
<input type="text" v-model="versions.build_ver" placeholder="2.180.0.0">
|
||||
<div style="font-size: 12px; color: #999; margin-top: 4px;">格式:主版本.次版本.修订号.构建号(App_Store 打包时构建号自动 +1)</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" style="width: auto; padding: 10px 32px; margin-bottom: 24px;" @click="saveVersions">保存版本号</button>
|
||||
|
||||
<h4 class="section-title">打包参数</h4>
|
||||
<div class="form-group">
|
||||
<label>最大并行打包数</label>
|
||||
<input type="number" v-model.number="buildSettings.max_concurrent_builds" min="1" max="4">
|
||||
@@ -439,6 +453,7 @@ const servers = ref({})
|
||||
const branches = ref([])
|
||||
const newBranch = ref('')
|
||||
const buildSettings = ref({})
|
||||
const versions = ref({ app_ver: '', build_ver: '' })
|
||||
const uploadConfig = ref({ mode: 'oss', oss: {}, webdav: {}, dingtalk: {} })
|
||||
const jsonContent = ref('{}')
|
||||
|
||||
@@ -459,7 +474,7 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
const loadData = async () => {
|
||||
const [appsRes, schemesRes, serversRes, branchesRes, buildRes, uploadRes, configRes] = await Promise.all([
|
||||
const [appsRes, schemesRes, serversRes, branchesRes, buildRes, uploadRes, configRes, versionsRes] = await Promise.all([
|
||||
fetch('/api/config/apps'),
|
||||
fetch('/api/config/schemes'),
|
||||
fetch('/api/config/servers'),
|
||||
@@ -467,6 +482,7 @@ const loadData = async () => {
|
||||
fetch('/api/config/build'),
|
||||
fetch('/api/config/upload'),
|
||||
fetch('/api/config'),
|
||||
fetch('/api/config/versions'),
|
||||
])
|
||||
apps.value = await appsRes.json()
|
||||
schemes.value = await schemesRes.json()
|
||||
@@ -475,6 +491,7 @@ const loadData = async () => {
|
||||
buildSettings.value = await buildRes.json()
|
||||
uploadConfig.value = await uploadRes.json()
|
||||
jsonContent.value = JSON.stringify(await configRes.json(), null, 2)
|
||||
versions.value = await versionsRes.json()
|
||||
}
|
||||
|
||||
// 服务器环境管理
|
||||
@@ -724,6 +741,26 @@ const saveBuildSettings = async () => {
|
||||
alert('设置已保存')
|
||||
}
|
||||
|
||||
const saveVersions = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/config/versions', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(versions.value),
|
||||
})
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
versions.value = { app_ver: data.app_ver, build_ver: data.build_ver }
|
||||
alert('版本号已保存')
|
||||
} else {
|
||||
const err = await res.json()
|
||||
alert(err.detail || '保存失败')
|
||||
}
|
||||
} catch (e) {
|
||||
alert('保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
const saveUploadConfig = async () => {
|
||||
try {
|
||||
await fetch('/api/config/upload', {
|
||||
|
||||
Reference in New Issue
Block a user