feat: update codex live automation and plan flows
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
import { getEnv } from '../config/env.js';
|
||||
import { db } from '../db/client.js';
|
||||
import {
|
||||
normalizeLegacyAutomationBehaviorType,
|
||||
resolveAutomationType,
|
||||
resolveStoredAutomationTypeId,
|
||||
type AutomationBehaviorType,
|
||||
} from './automation-type-config-service.js';
|
||||
import { shouldTriggerRetryFromActionNote } from './plan-retry-policy.js';
|
||||
|
||||
export const PLAN_TABLE = 'plan_items';
|
||||
@@ -53,6 +59,7 @@ type PlanRowOptions = {
|
||||
maskNote?: boolean;
|
||||
noteMasked?: boolean;
|
||||
releaseReviewNote?: string;
|
||||
exposeConfiguredAutomationType?: boolean;
|
||||
};
|
||||
|
||||
export const statusSchema = z.enum(planStatuses);
|
||||
@@ -62,32 +69,15 @@ export const setupSchema = z.object({
|
||||
});
|
||||
|
||||
function resolvePlanAutomationTypeAlias(value: unknown) {
|
||||
if (typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
|
||||
const normalizedValue = value.trim();
|
||||
|
||||
if (normalizedValue === 'plan_registration') {
|
||||
return 'plan';
|
||||
}
|
||||
|
||||
if (normalizedValue === 'general_development') {
|
||||
return 'auto_worker';
|
||||
}
|
||||
|
||||
return normalizedValue;
|
||||
return normalizeLegacyAutomationBehaviorType(value);
|
||||
}
|
||||
|
||||
export const planAutomationTypeSchema = z.preprocess(
|
||||
resolvePlanAutomationTypeAlias,
|
||||
z.enum(planAutomationTypes),
|
||||
);
|
||||
export const planAutomationTypeSchema = z.preprocess(resolvePlanAutomationTypeAlias, z.string().trim().min(1).max(120));
|
||||
|
||||
export const createPlanSchema = z.object({
|
||||
workId: z.string().trim().optional().default('작업ID'),
|
||||
note: z.string().default(''),
|
||||
automationType: z.preprocess(resolvePlanAutomationTypeAlias, z.enum(planAutomationTypes).default('none')),
|
||||
automationType: z.preprocess(resolvePlanAutomationTypeAlias, z.string().trim().min(1).max(120).default('none')),
|
||||
releaseTarget: z.string().trim().min(1).default('release'),
|
||||
jangsingProcessingRequired: z.boolean().default(true),
|
||||
autoDeployToMain: z.boolean().default(true),
|
||||
@@ -98,7 +88,7 @@ export const createPlanSchema = z.object({
|
||||
export const updatePlanSchema = z.object({
|
||||
workId: z.string().trim().optional(),
|
||||
note: z.string().optional(),
|
||||
automationType: z.preprocess(resolvePlanAutomationTypeAlias, z.enum(planAutomationTypes).optional()),
|
||||
automationType: z.preprocess(resolvePlanAutomationTypeAlias, z.string().trim().min(1).max(120).optional()),
|
||||
releaseTarget: z.string().trim().min(1).optional(),
|
||||
jangsingProcessingRequired: z.boolean().optional(),
|
||||
autoDeployToMain: z.boolean().optional(),
|
||||
@@ -135,7 +125,7 @@ export const listPlanQuerySchema = z.object({
|
||||
});
|
||||
|
||||
export type PlanStatus = (typeof planStatuses)[number];
|
||||
export type PlanAutomationType = (typeof planAutomationTypes)[number];
|
||||
export type PlanAutomationType = string;
|
||||
export type PlanWorkerStatus = (typeof planWorkerStatuses)[number];
|
||||
export type PlanReleaseReviewStatus = (typeof planReleaseReviewStatuses)[number];
|
||||
|
||||
@@ -191,8 +181,8 @@ function normalizePlanWorkId(value?: string | null) {
|
||||
|
||||
export function normalizePlanAutomationType(value: unknown): PlanAutomationType {
|
||||
const normalizedValue = resolvePlanAutomationTypeAlias(value);
|
||||
return planAutomationTypes.includes(normalizedValue as PlanAutomationType)
|
||||
? (normalizedValue as PlanAutomationType)
|
||||
return planAutomationTypes.includes(normalizedValue as AutomationBehaviorType)
|
||||
? (normalizedValue as AutomationBehaviorType)
|
||||
: 'none';
|
||||
}
|
||||
|
||||
@@ -281,7 +271,8 @@ export function mapPlanRow(
|
||||
id: row.id,
|
||||
workId: row.work_id,
|
||||
note: options?.maskNote ? maskPlanNote(row.note) : row.note,
|
||||
automationType: normalizePlanAutomationType(row.automation_type),
|
||||
automationType: options?.exposeConfiguredAutomationType ? resolveStoredAutomationTypeId(row) : normalizePlanAutomationType(row.automation_type),
|
||||
automationBehaviorType: normalizePlanAutomationType(row.automation_type),
|
||||
releaseReviewNote: options?.releaseReviewNote ?? '',
|
||||
noteMasked: Boolean(options?.noteMasked),
|
||||
status: row.status,
|
||||
@@ -825,6 +816,9 @@ async function syncPlanColumns() {
|
||||
await ensureColumn('automation_type', (table) => {
|
||||
table.string('automation_type', 40).notNullable().defaultTo('none');
|
||||
});
|
||||
await ensureColumn('automation_type_id', (table) => {
|
||||
table.string('automation_type_id', 120).nullable();
|
||||
});
|
||||
await ensureColumn('auto_deploy_to_main', (table) => {
|
||||
table.boolean('auto_deploy_to_main').notNullable().defaultTo(true);
|
||||
});
|
||||
@@ -871,6 +865,11 @@ async function syncPlanColumns() {
|
||||
await db(PLAN_TABLE)
|
||||
.where({ automation_type: 'general_development' })
|
||||
.update({ automation_type: 'auto_worker' });
|
||||
await db(PLAN_TABLE)
|
||||
.whereNull('automation_type_id')
|
||||
.update({
|
||||
automation_type_id: db.raw('automation_type'),
|
||||
});
|
||||
}
|
||||
|
||||
async function dropPlanWorkIdUniqueConstraint() {
|
||||
@@ -1090,13 +1089,15 @@ export async function ensurePlanTable() {
|
||||
export async function createPlanItem(payload: z.infer<typeof createPlanSchema>) {
|
||||
await ensurePlanTable();
|
||||
const workId = normalizePlanWorkId(payload.workId);
|
||||
const automationType = await resolveAutomationType(payload.automationType);
|
||||
|
||||
const rows = await db(PLAN_TABLE)
|
||||
.insert({
|
||||
work_id: workId,
|
||||
note: payload.note,
|
||||
status: '등록',
|
||||
automation_type: normalizePlanAutomationType(payload.automationType),
|
||||
automation_type: automationType.behaviorType,
|
||||
automation_type_id: automationType.id,
|
||||
release_target: payload.releaseTarget,
|
||||
jangsing_processing_required: payload.jangsingProcessingRequired,
|
||||
auto_deploy_to_main: payload.autoDeployToMain,
|
||||
@@ -1114,13 +1115,15 @@ export async function createPlanItem(payload: z.infer<typeof createPlanSchema>)
|
||||
export async function createCompletedPlanExecutionLogItem(payload: z.infer<typeof createPlanSchema>) {
|
||||
await ensurePlanTable();
|
||||
const workId = normalizePlanWorkId(payload.workId);
|
||||
const automationType = await resolveAutomationType(payload.automationType);
|
||||
|
||||
const rows = await db(PLAN_TABLE)
|
||||
.insert({
|
||||
work_id: workId,
|
||||
note: payload.note,
|
||||
status: '완료',
|
||||
automation_type: normalizePlanAutomationType(payload.automationType),
|
||||
automation_type: automationType.behaviorType,
|
||||
automation_type_id: automationType.id,
|
||||
release_target: payload.releaseTarget,
|
||||
jangsing_processing_required: payload.jangsingProcessingRequired,
|
||||
auto_deploy_to_main: payload.autoDeployToMain,
|
||||
@@ -1159,11 +1162,12 @@ export async function upsertAutoPlanItem(args: {
|
||||
.first();
|
||||
|
||||
if (!existingRow) {
|
||||
const automationType = await resolveAutomationType(args.automationType);
|
||||
return {
|
||||
row: await createPlanItem({
|
||||
workId,
|
||||
note: args.note,
|
||||
automationType: normalizePlanAutomationType(args.automationType),
|
||||
automationType: automationType.id,
|
||||
releaseTarget: args.releaseTarget,
|
||||
jangsingProcessingRequired: args.jangsingProcessingRequired,
|
||||
autoDeployToMain: args.autoDeployToMain,
|
||||
@@ -1175,7 +1179,7 @@ export async function upsertAutoPlanItem(args: {
|
||||
}
|
||||
|
||||
const nextReleaseTarget = args.releaseTarget || existingRow.release_target || 'release';
|
||||
const nextAutomationType = normalizePlanAutomationType(args.automationType ?? existingRow.automation_type);
|
||||
const nextAutomationType = await resolveAutomationType(args.automationType ?? existingRow.automation_type_id ?? existingRow.automation_type);
|
||||
const nextJangsingProcessingRequired = args.jangsingProcessingRequired;
|
||||
const nextAutoDeployToMain = args.autoDeployToMain;
|
||||
const nextNote = args.note;
|
||||
@@ -1185,7 +1189,7 @@ export async function upsertAutoPlanItem(args: {
|
||||
: existingRow.normal_processing_level === '상';
|
||||
const hasPayloadChange =
|
||||
existingRow.note !== nextNote ||
|
||||
normalizePlanAutomationType(existingRow.automation_type) !== nextAutomationType ||
|
||||
String(existingRow.automation_type_id ?? existingRow.automation_type ?? 'none') !== nextAutomationType.id ||
|
||||
(existingRow.release_target ?? 'release') !== nextReleaseTarget ||
|
||||
Boolean(existingRow.auto_deploy_to_main ?? true) !== nextAutoDeployToMain ||
|
||||
Boolean(currentJangsingProcessingRequired) !== nextJangsingProcessingRequired;
|
||||
@@ -1202,7 +1206,8 @@ export async function upsertAutoPlanItem(args: {
|
||||
.where({ id: existingRow.id })
|
||||
.update({
|
||||
note: nextNote,
|
||||
automation_type: nextAutomationType,
|
||||
automation_type: nextAutomationType.behaviorType,
|
||||
automation_type_id: nextAutomationType.id,
|
||||
release_target: nextReleaseTarget,
|
||||
jangsing_processing_required: nextJangsingProcessingRequired,
|
||||
auto_deploy_to_main: nextAutoDeployToMain,
|
||||
@@ -1223,7 +1228,8 @@ export async function upsertAutoPlanItem(args: {
|
||||
note: nextNote,
|
||||
status: '등록',
|
||||
assigned_branch: null,
|
||||
automation_type: nextAutomationType,
|
||||
automation_type: nextAutomationType.behaviorType,
|
||||
automation_type_id: nextAutomationType.id,
|
||||
release_target: nextReleaseTarget,
|
||||
jangsing_processing_required: nextJangsingProcessingRequired,
|
||||
auto_deploy_to_main: nextAutoDeployToMain,
|
||||
@@ -1262,7 +1268,9 @@ export async function updatePlanItem(id: number, payload: z.infer<typeof updateP
|
||||
|
||||
const nextWorkId =
|
||||
payload.workId === undefined ? currentRow.work_id : normalizePlanWorkId(payload.workId);
|
||||
const nextAutomationType = payload.automationType ?? normalizePlanAutomationType(currentRow.automation_type);
|
||||
const nextAutomationType = await resolveAutomationType(
|
||||
payload.automationType ?? currentRow.automation_type_id ?? currentRow.automation_type,
|
||||
);
|
||||
const nextReleaseTarget = payload.releaseTarget ?? currentRow.release_target ?? 'release';
|
||||
const nextJangsingProcessingRequired =
|
||||
payload.jangsingProcessingRequired ??
|
||||
@@ -1276,7 +1284,7 @@ export async function updatePlanItem(id: number, payload: z.infer<typeof updateP
|
||||
|
||||
const isOnlyJangsingUpdate =
|
||||
nextWorkId === currentRow.work_id &&
|
||||
nextAutomationType === normalizePlanAutomationType(currentRow.automation_type) &&
|
||||
nextAutomationType.id === String(currentRow.automation_type_id ?? currentRow.automation_type ?? 'none') &&
|
||||
nextReleaseTarget === (currentRow.release_target ?? 'release') &&
|
||||
nextAutoDeployToMain === (currentRow.auto_deploy_to_main ?? true) &&
|
||||
nextRepeatRequestEnabled === (currentRow.repeat_request_enabled ?? false) &&
|
||||
@@ -1297,7 +1305,8 @@ export async function updatePlanItem(id: number, payload: z.infer<typeof updateP
|
||||
work_id: nextWorkId,
|
||||
note: nextNote,
|
||||
status: currentRow.status,
|
||||
automation_type: nextAutomationType,
|
||||
automation_type: nextAutomationType.behaviorType,
|
||||
automation_type_id: nextAutomationType.id,
|
||||
release_target: nextReleaseTarget,
|
||||
jangsing_processing_required: nextJangsingProcessingRequired,
|
||||
auto_deploy_to_main: nextAutoDeployToMain,
|
||||
@@ -2261,6 +2270,7 @@ export async function listPlanReleaseReviewBoardItems(options?: Pick<PlanRowOpti
|
||||
...(issueSummaryMap.get(planItemId) ?? { issueTags: [], hasOpenIssues: false }),
|
||||
maskNote: options?.maskNote,
|
||||
noteMasked: options?.maskNote,
|
||||
exposeConfiguredAutomationType: true,
|
||||
});
|
||||
const latestSourceWork = latestSourceWorkMap.get(planItemId) ?? null;
|
||||
const reviewRow = reviewRowMap.get(planItemId);
|
||||
@@ -2818,6 +2828,7 @@ export async function listPlanItems(status?: PlanStatus, options?: Pick<PlanRowO
|
||||
maskNote: options?.maskNote,
|
||||
noteMasked: options?.maskNote,
|
||||
releaseReviewNote: releaseReviewNoteMap.get(Number(row.id)) ?? '',
|
||||
exposeConfiguredAutomationType: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -2843,6 +2854,7 @@ export async function getPlanItemById(id: number, options?: Pick<PlanRowOptions,
|
||||
maskNote: options?.maskNote,
|
||||
noteMasked: options?.maskNote,
|
||||
releaseReviewNote: releaseReviewNoteMap.get(id) ?? '',
|
||||
exposeConfiguredAutomationType: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user