♻️ refactor(vscode): 设置项 autoBuildBranch 换成发布分支前缀与 Jenkins 任务
Claude-Session: https://claude.ai/code/session_01HTMgwaE3woqM5yvsHiuSAa
This commit is contained in:
1 parent
fa32d4a88b
commit
6f59b4f3fd
13 files changed
+149
-66
No files matched your search
@@ -865,7 +865,9 @@ export class KanbanWebviewPanel {
|
||||
autoReview: s.autoReview,
|
||||
reviewPrompt: s.reviewPrompt,
|
||||
devBranch: s.devBranch,
|
||||
autoBuildBranch: s.autoBuildBranch,
|
||||
releaseBranchPrefix: s.releaseBranchPrefix,
|
||||
jenkinsJob: s.jenkinsJob,
|
||||
jenkinsProdHost: s.jenkinsProdHost,
|
||||
worktreeDirectory: s.worktreeDirectory,
|
||||
worktreeOpenWith: s.worktreeOpenWith,
|
||||
pycharmCommand: s.pycharmCommand,
|
||||
@@ -945,7 +947,9 @@ export class KanbanWebviewPanel {
|
||||
autoReview: s.autoReview,
|
||||
reviewPrompt: s.reviewPrompt,
|
||||
devBranch: s.devBranch,
|
||||
autoBuildBranch: s.autoBuildBranch,
|
||||
releaseBranchPrefix: s.releaseBranchPrefix,
|
||||
jenkinsJob: s.jenkinsJob,
|
||||
jenkinsProdHost: s.jenkinsProdHost,
|
||||
worktreeDirectory: s.worktreeDirectory,
|
||||
worktreeOpenWith: s.worktreeOpenWith,
|
||||
pycharmCommand: s.pycharmCommand,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { logger } from '../../logging/logger'
|
||||
/**
|
||||
* 清理一个 feature 分支(远程 + 本地),供「拖到完成」和「手动删除 worktree」两处共用。
|
||||
*
|
||||
* - 受保护分支(main/master/devBranch/autoBuildBranch)直接跳过,绝不删。
|
||||
* - 受保护分支(main/master/devBranch)直接跳过,绝不删。
|
||||
* - 远程删除走 gitea API,需 remote + token;缺任一则只删本地,不阻断。
|
||||
* - 本地删除是强删(deleteLocalBranch 内部 `git branch -D`):调用方语义是「连分支一起删」,
|
||||
* 未合并也照删。
|
||||
@@ -20,17 +20,16 @@ export async function cleanupFeatureBranch(opts: {
|
||||
branch: string
|
||||
issueNumber: number
|
||||
devBranch?: string
|
||||
autoBuildBranch?: string
|
||||
/** 缺 remote 或 token 时跳过远程删除,只删本地。 */
|
||||
remote?: { host: string, owner: string, repo: string }
|
||||
token?: string
|
||||
}): Promise<void> {
|
||||
const { workspaceRoot, branch, issueNumber, devBranch, autoBuildBranch, remote, token } = opts
|
||||
const { workspaceRoot, branch, issueNumber, devBranch, remote, token } = opts
|
||||
if (!branch)
|
||||
return
|
||||
|
||||
const protectedBranches = new Set(
|
||||
['main', 'master', devBranch, autoBuildBranch].filter((b): b is string => Boolean(b)),
|
||||
['main', 'master', devBranch].filter((b): b is string => Boolean(b)),
|
||||
)
|
||||
if (protectedBranches.has(branch)) {
|
||||
logger.add({ level: 'warn', source: 'panel', message: `跳过受保护分支清理 ${branch} (issue #${issueNumber})` })
|
||||
|
||||
@@ -342,7 +342,7 @@ export async function handleHandoffStart(panel: KanbanWebviewPanel, issueNumber:
|
||||
}
|
||||
if (branch) {
|
||||
const settings = getSettings(panel.context)
|
||||
await cleanupFeatureBranch({ workspaceRoot, branch, issueNumber, devBranch: settings.devBranch, autoBuildBranch: settings.autoBuildBranch })
|
||||
await cleanupFeatureBranch({ workspaceRoot, branch, issueNumber, devBranch: settings.devBranch })
|
||||
}
|
||||
|
||||
// ⑦ 本机墓碑:sid/profile/worktreePath 归零。worktree 没删掉时保留
|
||||
|
||||
@@ -506,7 +506,6 @@ export async function handleColumnChange(panel: KanbanWebviewPanel, issueNumber:
|
||||
branch: featureBranch,
|
||||
issueNumber,
|
||||
devBranch: settingsForBranchCleanup.devBranch,
|
||||
autoBuildBranch: settingsForBranchCleanup.autoBuildBranch,
|
||||
remote,
|
||||
token,
|
||||
})
|
||||
@@ -786,7 +785,6 @@ export async function handleResetToTodo(panel: KanbanWebviewPanel, issueNumber:
|
||||
branch: featureBranch,
|
||||
issueNumber,
|
||||
devBranch: settingsForBranchCleanup.devBranch,
|
||||
autoBuildBranch: settingsForBranchCleanup.autoBuildBranch,
|
||||
remote,
|
||||
token,
|
||||
})
|
||||
|
||||
@@ -761,7 +761,6 @@ export async function handleImplement(
|
||||
branch,
|
||||
issueNumber,
|
||||
devBranch: settings.devBranch,
|
||||
autoBuildBranch: settings.autoBuildBranch,
|
||||
remote,
|
||||
token,
|
||||
})
|
||||
|
||||
@@ -28,7 +28,9 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
autoReview: boolean
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
releaseBranchPrefix: string
|
||||
jenkinsJob: string
|
||||
jenkinsProdHost: string
|
||||
worktreeDirectory: string
|
||||
worktreeOpenWith: 'vscode' | 'pycharm'
|
||||
pycharmCommand: string
|
||||
@@ -55,7 +57,10 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
const trimmedYtClose = payload.youtrackCloseCommand.trim()
|
||||
const trimmedYtToken = payload.youtrackToken.trim()
|
||||
const trimmedDevBranch = payload.devBranch.trim()
|
||||
const trimmedAutoBuildBranch = payload.autoBuildBranch.trim()
|
||||
// 发布前缀 / Jenkins 任务 / 正式服 host:只 trim,空值回退在 getSettings 读时做。
|
||||
const trimmedReleasePrefix = payload.releaseBranchPrefix.trim()
|
||||
const trimmedJenkinsJob = payload.jenkinsJob.trim()
|
||||
const trimmedJenkinsProdHost = payload.jenkinsProdHost.trim()
|
||||
// worktree dir template: '' = "use default", so don't preserve blanks
|
||||
// specially — getSettings falls back to the default at read time.
|
||||
const trimmedWorktreeDir = payload.worktreeDirectory.trim()
|
||||
@@ -101,7 +106,9 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
autoReview: payload.autoReview,
|
||||
reviewPrompt: payload.reviewPrompt || prev.reviewPrompt,
|
||||
devBranch: trimmedDevBranch || prev.devBranch,
|
||||
autoBuildBranch: trimmedAutoBuildBranch,
|
||||
releaseBranchPrefix: trimmedReleasePrefix || prev.releaseBranchPrefix,
|
||||
jenkinsJob: trimmedJenkinsJob,
|
||||
jenkinsProdHost: trimmedJenkinsProdHost || prev.jenkinsProdHost,
|
||||
worktreeDirectory: trimmedWorktreeDir || prev.worktreeDirectory,
|
||||
worktreeOpenWith: payload.worktreeOpenWith || prev.worktreeOpenWith,
|
||||
pycharmCommand: trimmedPycharmCmd || prev.pycharmCommand,
|
||||
@@ -126,11 +133,10 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
implementPlanPrompt: payload.implementPlanPrompt,
|
||||
autoReview: payload.autoReview,
|
||||
reviewPrompt: payload.reviewPrompt,
|
||||
// Persist trimmed values; '' is meaningful for autoBuildBranch
|
||||
// ("follow devBranch"), so don't coerce — getSettings handles the
|
||||
// fallback at read time.
|
||||
devBranch: trimmedDevBranch,
|
||||
autoBuildBranch: trimmedAutoBuildBranch,
|
||||
releaseBranchPrefix: trimmedReleasePrefix,
|
||||
jenkinsJob: trimmedJenkinsJob,
|
||||
jenkinsProdHost: trimmedJenkinsProdHost,
|
||||
worktreeDirectory: trimmedWorktreeDir,
|
||||
worktreeOpenWith: payload.worktreeOpenWith,
|
||||
pycharmCommand: trimmedPycharmCmd,
|
||||
@@ -233,7 +239,9 @@ export async function handleEditSettingsRequest(panel: KanbanWebviewPanel): Prom
|
||||
autoReview: s.autoReview,
|
||||
reviewPrompt: s.reviewPrompt,
|
||||
devBranch: s.devBranch,
|
||||
autoBuildBranch: s.autoBuildBranch,
|
||||
releaseBranchPrefix: s.releaseBranchPrefix,
|
||||
jenkinsJob: s.jenkinsJob,
|
||||
jenkinsProdHost: s.jenkinsProdHost,
|
||||
worktreeDirectory: s.worktreeDirectory,
|
||||
worktreeOpenWith: s.worktreeOpenWith,
|
||||
pycharmCommand: s.pycharmCommand,
|
||||
|
||||
@@ -139,7 +139,6 @@ export async function handleDeleteWorktree(panel: KanbanWebviewPanel, issueNumbe
|
||||
branch,
|
||||
issueNumber,
|
||||
devBranch: settingsForHook.devBranch,
|
||||
autoBuildBranch: settingsForHook.autoBuildBranch,
|
||||
remote: remote ? { host: remote.host, owner: remote.owner, repo: remote.repo } : undefined,
|
||||
token,
|
||||
})
|
||||
|
||||
@@ -73,7 +73,9 @@ export type ExtensionToWebview
|
||||
autoReview: boolean
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
releaseBranchPrefix: string
|
||||
jenkinsJob: string
|
||||
jenkinsProdHost: string
|
||||
worktreeDirectory: string
|
||||
worktreeOpenWith: 'vscode' | 'pycharm'
|
||||
pycharmCommand: string
|
||||
@@ -109,13 +111,15 @@ export type ExtensionToWebview
|
||||
| { type: 'logs/cleared' }
|
||||
| { type: 'commit/state', running: boolean }
|
||||
| { type: 'commit/has-changes', value: boolean }
|
||||
// 发布点状态:nextBranch 是今天要切的分支全名,由扩展侧算好,webview 不自己拼日期。
|
||||
| {
|
||||
type: 'branch-sync/status'
|
||||
behind: number
|
||||
type: 'release/status'
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
unavailable?: boolean
|
||||
reason?: string
|
||||
current?: string
|
||||
nextBranch: string
|
||||
unreleased: number
|
||||
unavailable?: string
|
||||
running: boolean
|
||||
}
|
||||
| { type: 'env-lock/status', locked: boolean, fileCount: number, failedCount?: number }
|
||||
| { type: 'issue/remove', issueNumber: number }
|
||||
@@ -145,7 +149,9 @@ export type WebviewToExtension
|
||||
autoReview: boolean
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
releaseBranchPrefix: string
|
||||
jenkinsJob: string
|
||||
jenkinsProdHost: string
|
||||
worktreeDirectory: string
|
||||
worktreeOpenWith: 'vscode' | 'pycharm'
|
||||
pycharmCommand: string
|
||||
@@ -200,8 +206,10 @@ export type WebviewToExtension
|
||||
| { type: 'logs/clear' }
|
||||
| { type: 'commit/run' }
|
||||
| { type: 'session/close-tab', issueNumber: number, kind: 'brainstorm' | 'implement' | 'review' | 'test' }
|
||||
| { type: 'branch-sync/check' }
|
||||
| { type: 'branch-sync/run' }
|
||||
| { type: 'release/check' }
|
||||
| { type: 'release/cut' }
|
||||
| { type: 'release/cherry-pick' }
|
||||
| { type: 'release/deploy' }
|
||||
| { type: 'env-lock/check' }
|
||||
| { type: 'env-lock/toggle' }
|
||||
| { type: 'issue/delete', issueNumber: number }
|
||||
|
||||
@@ -15,6 +15,7 @@ import { readFileSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { seedCommitProfilePath } from '../cc/commitProfile'
|
||||
import { listClaudeProfiles } from '../cc/profiles'
|
||||
import { normalizeReleasePrefix } from '../git/release'
|
||||
|
||||
/**
|
||||
* Fallback prompt strings used when the on-disk markdown can't be read.
|
||||
@@ -88,11 +89,14 @@ export interface Settings {
|
||||
/** Day-to-day development branch (e.g. `main`). Jenkins doesn't watch it. */
|
||||
devBranch: string
|
||||
/**
|
||||
* Branch the Gitea webhook → Jenkins job listens on. Empty string means
|
||||
* "same as `devBranch`", in which case the branch-sync button stays
|
||||
* disabled because there's nothing to fast-forward.
|
||||
* 发布分支名前缀(如 `release/`),远端该前缀下字典序最大的分支即当期发布点。
|
||||
* 读出时已归一化:trim、空回退默认、末尾补 `/`。
|
||||
*/
|
||||
autoBuildBranch: string
|
||||
releaseBranchPrefix: string
|
||||
/** 正式服部署的 Jenkins 任务名;空串 = 不提供「部署到正式服」。 */
|
||||
jenkinsJob: string
|
||||
/** Jenkins 任务的 `REMOTE_HOST` 参数取值,空回退 `正式服`。 */
|
||||
jenkinsProdHost: string
|
||||
/**
|
||||
* Directory template for the worktree created when implementing a plan.
|
||||
* Placeholders: `$project_root` (workspace abs path), `$project_name` (its
|
||||
@@ -215,7 +219,9 @@ function defaults(ctx: ExtensionContext): Settings {
|
||||
autoReview: true,
|
||||
reviewPrompt: readDefaultPrompt(ctx.extensionPath, 'review'),
|
||||
devBranch: 'main',
|
||||
autoBuildBranch: '',
|
||||
releaseBranchPrefix: 'release/',
|
||||
jenkinsJob: '',
|
||||
jenkinsProdHost: '正式服',
|
||||
worktreeDirectory: '~/Sources/worktree/$project_name/$feature_name',
|
||||
worktreeOpenWith: 'vscode',
|
||||
pycharmCommand: 'pycharm',
|
||||
@@ -266,12 +272,17 @@ export function getSettings(ctx: ExtensionContext): Settings {
|
||||
const devBranch = typeof stored.devBranch === 'string' && stored.devBranch.length > 0
|
||||
? stored.devBranch
|
||||
: base.devBranch
|
||||
// autoBuildBranch is kept as-is — '' is meaningful ("follow devBranch, sync
|
||||
// disabled"), so we don't coerce to a default here. Anything non-string
|
||||
// (legacy installs that never wrote it) collapses to ''.
|
||||
const autoBuildBranch = typeof stored.autoBuildBranch === 'string'
|
||||
? stored.autoBuildBranch
|
||||
: base.autoBuildBranch
|
||||
// 发布前缀读出时归一化(空 → 默认、补尾 `/`),下游拼分支名不用再防御。
|
||||
const releaseBranchPrefix = normalizeReleasePrefix(
|
||||
typeof stored.releaseBranchPrefix === 'string' ? stored.releaseBranchPrefix : base.releaseBranchPrefix,
|
||||
)
|
||||
// jenkinsJob:'' 有意义(= 不提供部署动作),只认字符串类型。
|
||||
const jenkinsJob = typeof stored.jenkinsJob === 'string'
|
||||
? stored.jenkinsJob.trim()
|
||||
: base.jenkinsJob
|
||||
const jenkinsProdHost = typeof stored.jenkinsProdHost === 'string' && stored.jenkinsProdHost.trim().length > 0
|
||||
? stored.jenkinsProdHost.trim()
|
||||
: base.jenkinsProdHost
|
||||
// worktree dir template: empty/blank means "use the default template", so
|
||||
// fall back to base (unlike the hook paths below, where '' is meaningful).
|
||||
const worktreeDirectory = typeof stored.worktreeDirectory === 'string' && stored.worktreeDirectory.length > 0
|
||||
@@ -349,7 +360,9 @@ export function getSettings(ctx: ExtensionContext): Settings {
|
||||
autoReview,
|
||||
reviewPrompt,
|
||||
devBranch,
|
||||
autoBuildBranch,
|
||||
releaseBranchPrefix,
|
||||
jenkinsJob,
|
||||
jenkinsProdHost,
|
||||
worktreeDirectory,
|
||||
worktreeOpenWith,
|
||||
pycharmCommand,
|
||||
|
||||
Reference in new issue
Block a user