✨ feat(vscode): 同一天再切发布直接把 main 新提交推进到当天分支,不再另起编号分支
This commit is contained in:
1 parent
4aae4df1f1
commit
17570749dd
4 files changed
+128
-94
No files matched your search
@@ -20,7 +20,7 @@ import {
|
||||
indexUnreleasedByMergePr,
|
||||
listReleaseBranches,
|
||||
listUnreleasedCommits,
|
||||
nextReleaseBranch,
|
||||
todayReleaseBranch,
|
||||
} from '../../git/release'
|
||||
import { DEFAULT_WORKTREE_TEMPLATE, resolveWorktreeDir } from '../../git/worktreePath'
|
||||
import { triggerJenkinsBuild } from '../../jenkins/trigger'
|
||||
@@ -35,7 +35,7 @@ export async function handleReleaseStatus(panel: KanbanWebviewPanel): Promise<vo
|
||||
const base = {
|
||||
type: 'release/status' as const,
|
||||
devBranch: s.devBranch,
|
||||
nextBranch: nextReleaseBranch(s.releaseBranchPrefix, []),
|
||||
nextBranch: todayReleaseBranch(s.releaseBranchPrefix),
|
||||
running: panel.releaseRunning,
|
||||
jenkinsConfigured: s.jenkinsJob.length > 0,
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export async function handleReleaseStatus(panel: KanbanWebviewPanel): Promise<vo
|
||||
}
|
||||
try {
|
||||
const st = await getReleaseStatus({ cwd: root, devBranch: s.devBranch, prefix: s.releaseBranchPrefix })
|
||||
panel.postMessage({ ...base, nextBranch: st.next, current: st.current, unreleased: st.unreleased, unavailable: st.unavailable })
|
||||
panel.postMessage({ ...base, current: st.current, unreleased: st.unreleased, unavailable: st.unavailable })
|
||||
}
|
||||
catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err)
|
||||
@@ -88,13 +88,26 @@ export function handleReleaseCut(panel: KanbanWebviewPanel): Promise<void> {
|
||||
return runReleaseAction(panel, async () => {
|
||||
const root = requireRoot()
|
||||
const s = getSettings(panel.context)
|
||||
const { branch, count } = await cutRelease({ cwd: root, devBranch: s.devBranch, prefix: s.releaseBranchPrefix })
|
||||
toast(panel, 'success', `已切发布分支 ${branch}(${count} 个提交)`)
|
||||
const r = await cutRelease({ cwd: root, devBranch: s.devBranch, prefix: s.releaseBranchPrefix })
|
||||
if (r.kind === 'diverged') {
|
||||
const picked = await pickUnreleased(panel, { root, devBranch: s.devBranch, current: r.branch })
|
||||
if (picked !== 'done')
|
||||
return
|
||||
}
|
||||
else if (r.kind === 'advanced' && r.count === 0) {
|
||||
toast(panel, 'info', `${r.branch} 已经和 ${s.devBranch} 一致,没有新提交`)
|
||||
return
|
||||
}
|
||||
else {
|
||||
toast(panel, 'success', r.kind === 'created'
|
||||
? `已切发布分支 ${r.branch}(${r.count} 个提交)`
|
||||
: `已把 ${s.devBranch} 上 ${r.count} 个新提交推进到 ${r.branch}`)
|
||||
}
|
||||
if (!s.jenkinsJob)
|
||||
return
|
||||
const pick = await window.showInformationMessage(`现在部署 ${branch} 到${s.jenkinsProdHost}?`, '部署')
|
||||
const pick = await window.showInformationMessage(`现在部署 ${r.branch} 到${s.jenkinsProdHost}?`, '部署')
|
||||
if (pick === '部署')
|
||||
await deploy(panel, branch)
|
||||
await deploy(panel, r.branch)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -140,35 +153,48 @@ export function handleReleaseCherryPickConfirm(panel: KanbanWebviewPanel, shas:
|
||||
const status = await getReleaseStatus({ cwd: root, devBranch: s.devBranch, prefix: s.releaseBranchPrefix })
|
||||
if (status.unavailable)
|
||||
throw new Error(status.unavailable)
|
||||
const current = status.current
|
||||
if (!current)
|
||||
if (!status.current)
|
||||
throw new Error('尚无发布分支,先切新发布')
|
||||
const commits = await listUnreleasedCommits({ cwd: root, devBranch: s.devBranch, current })
|
||||
const wanted = new Set(shas)
|
||||
const blocked = commits.find(c => wanted.has(c.sha) && c.migration?.blocked)
|
||||
if (blocked)
|
||||
throw new Error(`${blocked.sha.slice(0, 7)} ${blocked.subject}:${blocked.migration!.blocked}`)
|
||||
const order = new Map(commits.map((c, i) => [c.sha, i]))
|
||||
const ordered = [...shas].sort((a, b) => (order.get(b) ?? 0) - (order.get(a) ?? 0))
|
||||
// 发布 worktree 走和实施 worktree 同一套目录模板,分支名里的 / 换成 - 当目录名。
|
||||
const worktreePath = resolveWorktreeDir(s.worktreeDirectory || DEFAULT_WORKTREE_TEMPLATE, root, current.replace(/\//g, '-'))
|
||||
const spinner = toast(panel, 'info', `正在摘 ${ordered.length} 个提交到 ${current}…`, { spinner: true, dismissOnTimer: 0 })
|
||||
let conflict: CherryPickConflict | undefined
|
||||
try {
|
||||
conflict = await cherryPickToRelease({ cwd: root, branch: current, shas: ordered, worktreePath })
|
||||
}
|
||||
finally {
|
||||
dismiss(panel, spinner)
|
||||
}
|
||||
if (!conflict) {
|
||||
toast(panel, 'success', `已摘 ${ordered.length} 个提交到 ${current}`)
|
||||
return
|
||||
}
|
||||
openReleaseConflictSession(panel, { branch: current, conflict, subjectOf: new Map(commits.map(c => [c.sha, c.subject])) })
|
||||
toast(panel, 'info', `${conflict.sha.slice(0, 7)} ${conflict.subject} 冲突,已开 ${current} 冲突解决会话接手`, { dismissOnTimer: 10_000 })
|
||||
await pickUnreleased(panel, { root, devBranch: s.devBranch, current: status.current, shas })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 把 devBranch 上未发布的提交摘到 current,shas 不传就全摘。
|
||||
* 冲突时开会话接手并返回 'conflict',调用方据此决定后续(比如要不要问部署)。
|
||||
*/
|
||||
async function pickUnreleased(panel: KanbanWebviewPanel, o: { root: string, devBranch: string, current: string, shas?: string[] }): Promise<'done' | 'conflict' | 'nothing'> {
|
||||
const s = getSettings(panel.context)
|
||||
const commits = await listUnreleasedCommits({ cwd: o.root, devBranch: o.devBranch, current: o.current })
|
||||
const wanted = o.shas && new Set(o.shas)
|
||||
const chosen = wanted ? commits.filter(c => wanted.has(c.sha)) : commits
|
||||
if (chosen.length === 0) {
|
||||
toast(panel, 'info', '没有待摘的提交')
|
||||
return 'nothing'
|
||||
}
|
||||
const blocked = chosen.find(c => c.migration?.blocked)
|
||||
if (blocked)
|
||||
throw new Error(`${blocked.sha.slice(0, 7)} ${blocked.subject}:${blocked.migration!.blocked}`)
|
||||
const ordered = chosen.map(c => c.sha).reverse() // ← 列表是新→旧,摘要旧→新
|
||||
// 发布 worktree 走和实施 worktree 同一套目录模板,分支名里的 / 换成 - 当目录名。
|
||||
const worktreePath = resolveWorktreeDir(s.worktreeDirectory || DEFAULT_WORKTREE_TEMPLATE, o.root, o.current.replace(/\//g, '-'))
|
||||
const spinner = toast(panel, 'info', `正在摘 ${ordered.length} 个提交到 ${o.current}…`, { spinner: true, dismissOnTimer: 0 })
|
||||
let conflict: CherryPickConflict | undefined
|
||||
try {
|
||||
conflict = await cherryPickToRelease({ cwd: o.root, branch: o.current, shas: ordered, worktreePath })
|
||||
}
|
||||
finally {
|
||||
dismiss(panel, spinner)
|
||||
}
|
||||
if (!conflict) {
|
||||
toast(panel, 'success', `已摘 ${ordered.length} 个提交到 ${o.current}`)
|
||||
return 'done'
|
||||
}
|
||||
openReleaseConflictSession(panel, { branch: o.current, conflict, subjectOf: new Map(commits.map(c => [c.sha, c.subject])) })
|
||||
toast(panel, 'info', `${conflict.sha.slice(0, 7)} ${conflict.subject} 冲突,已开 ${o.current} 冲突解决会话接手`, { dismissOnTimer: 10_000 })
|
||||
return 'conflict'
|
||||
}
|
||||
|
||||
/**
|
||||
* 摘修复冲突后的接力:worktree 里 cherry-pick 停在中间态,开一个 cc 会话进去解决、
|
||||
* 按原顺序摘完剩余提交并推远端。同名终端还活着就把提示词直接打进去,不再新开。
|
||||
|
||||
Reference in new issue
Block a user