mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
refactor: normalize branch name handling and enhance auto mode settings merging
- Updated branch name normalization to align with UI conventions, treating "main" as null for consistency. - Implemented deep merging of `autoModeByWorktree` settings to preserve existing entries during updates. - Enhanced the BoardView component to persist max concurrency settings to the server, ensuring accurate capacity checks. - Added error handling for feature rollback persistence in useBoardActions. These changes improve the reliability and consistency of auto mode settings across the application.
This commit is contained in:
@@ -534,7 +534,11 @@ export class AutoModeService {
|
|||||||
const autoModeByWorktree = settings.autoModeByWorktree;
|
const autoModeByWorktree = settings.autoModeByWorktree;
|
||||||
|
|
||||||
if (projectId && autoModeByWorktree && typeof autoModeByWorktree === 'object') {
|
if (projectId && autoModeByWorktree && typeof autoModeByWorktree === 'object') {
|
||||||
const key = `${projectId}::${branchName ?? '__main__'}`;
|
// Normalize branch name to match UI convention:
|
||||||
|
// - null or "main" -> "__main__" (UI treats "main" as the main worktree)
|
||||||
|
// This ensures consistency with how the UI stores worktree settings
|
||||||
|
const normalizedBranch = branchName === 'main' ? null : branchName;
|
||||||
|
const key = `${projectId}::${normalizedBranch ?? '__main__'}`;
|
||||||
const entry = autoModeByWorktree[key];
|
const entry = autoModeByWorktree[key];
|
||||||
if (entry && typeof entry.maxConcurrency === 'number') {
|
if (entry && typeof entry.maxConcurrency === 'number') {
|
||||||
return entry.maxConcurrency;
|
return entry.maxConcurrency;
|
||||||
@@ -1039,7 +1043,9 @@ export class AutoModeService {
|
|||||||
}> {
|
}> {
|
||||||
// Load feature to get branchName
|
// Load feature to get branchName
|
||||||
const feature = await this.loadFeature(projectPath, featureId);
|
const feature = await this.loadFeature(projectPath, featureId);
|
||||||
const branchName = feature?.branchName ?? null;
|
const rawBranchName = feature?.branchName ?? null;
|
||||||
|
// Normalize "main" to null to match UI convention for main worktree
|
||||||
|
const branchName = rawBranchName === 'main' ? null : rawBranchName;
|
||||||
|
|
||||||
// Get per-worktree limit
|
// Get per-worktree limit
|
||||||
const maxAgents = await this.resolveMaxConcurrency(projectPath, branchName);
|
const maxAgents = await this.resolveMaxConcurrency(projectPath, branchName);
|
||||||
|
|||||||
@@ -621,6 +621,21 @@ export class SettingsService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deep merge autoModeByWorktree if provided (preserves other worktree entries)
|
||||||
|
if (sanitizedUpdates.autoModeByWorktree) {
|
||||||
|
type WorktreeEntry = { maxConcurrency: number; branchName: string | null };
|
||||||
|
const mergedAutoModeByWorktree: Record<string, WorktreeEntry> = {
|
||||||
|
...current.autoModeByWorktree,
|
||||||
|
};
|
||||||
|
for (const [key, value] of Object.entries(sanitizedUpdates.autoModeByWorktree)) {
|
||||||
|
mergedAutoModeByWorktree[key] = {
|
||||||
|
...mergedAutoModeByWorktree[key],
|
||||||
|
...value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
updated.autoModeByWorktree = mergedAutoModeByWorktree;
|
||||||
|
}
|
||||||
|
|
||||||
await writeSettingsJson(settingsPath, updated);
|
await writeSettingsJson(settingsPath, updated);
|
||||||
logger.info('Global settings updated');
|
logger.info('Global settings updated');
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ import { usePipelineConfig } from '@/hooks/queries';
|
|||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { queryKeys } from '@/lib/query-keys';
|
import { queryKeys } from '@/lib/query-keys';
|
||||||
import { useAutoModeQueryInvalidation } from '@/hooks/use-query-invalidation';
|
import { useAutoModeQueryInvalidation } from '@/hooks/use-query-invalidation';
|
||||||
|
import { useUpdateGlobalSettings } from '@/hooks/mutations/use-settings-mutations';
|
||||||
|
|
||||||
// Stable empty array to avoid infinite loop in selector
|
// Stable empty array to avoid infinite loop in selector
|
||||||
const EMPTY_WORKTREES: ReturnType<ReturnType<typeof useAppStore.getState>['getWorktrees']> = [];
|
const EMPTY_WORKTREES: ReturnType<ReturnType<typeof useAppStore.getState>['getWorktrees']> = [];
|
||||||
@@ -451,6 +452,8 @@ export function BoardView() {
|
|||||||
const maxConcurrency = autoMode.maxConcurrency;
|
const maxConcurrency = autoMode.maxConcurrency;
|
||||||
// Get worktree-specific setter
|
// Get worktree-specific setter
|
||||||
const setMaxConcurrencyForWorktree = useAppStore((state) => state.setMaxConcurrencyForWorktree);
|
const setMaxConcurrencyForWorktree = useAppStore((state) => state.setMaxConcurrencyForWorktree);
|
||||||
|
// Mutation to persist maxConcurrency to server settings
|
||||||
|
const updateGlobalSettings = useUpdateGlobalSettings({ showSuccessToast: false });
|
||||||
|
|
||||||
// Get the current branch from the selected worktree (not from store which may be stale)
|
// Get the current branch from the selected worktree (not from store which may be stale)
|
||||||
const currentWorktreeBranch = selectedWorktree?.branch ?? null;
|
const currentWorktreeBranch = selectedWorktree?.branch ?? null;
|
||||||
@@ -1277,6 +1280,15 @@ export function BoardView() {
|
|||||||
if (currentProject && selectedWorktree) {
|
if (currentProject && selectedWorktree) {
|
||||||
const branchName = selectedWorktree.isMain ? null : selectedWorktree.branch;
|
const branchName = selectedWorktree.isMain ? null : selectedWorktree.branch;
|
||||||
setMaxConcurrencyForWorktree(currentProject.id, branchName, newMaxConcurrency);
|
setMaxConcurrencyForWorktree(currentProject.id, branchName, newMaxConcurrency);
|
||||||
|
|
||||||
|
// Persist to server settings so capacity checks use the correct value
|
||||||
|
const worktreeKey = `${currentProject.id}::${branchName ?? '__main__'}`;
|
||||||
|
updateGlobalSettings.mutate({
|
||||||
|
autoModeByWorktree: {
|
||||||
|
[worktreeKey]: { maxConcurrency: newMaxConcurrency },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Also update backend if auto mode is running
|
// Also update backend if auto mode is running
|
||||||
if (autoMode.isRunning) {
|
if (autoMode.isRunning) {
|
||||||
// Restart auto mode with new concurrency (backend will handle this)
|
// Restart auto mode with new concurrency (backend will handle this)
|
||||||
|
|||||||
@@ -553,6 +553,11 @@ export function useBoardActions({
|
|||||||
};
|
};
|
||||||
updateFeature(feature.id, rollbackUpdates);
|
updateFeature(feature.id, rollbackUpdates);
|
||||||
|
|
||||||
|
// Also persist the rollback so it survives page refresh
|
||||||
|
persistFeatureUpdate(feature.id, rollbackUpdates).catch((persistError) => {
|
||||||
|
logger.error('Failed to persist rollback:', persistError);
|
||||||
|
});
|
||||||
|
|
||||||
// If server is offline (connection refused), redirect to login page
|
// If server is offline (connection refused), redirect to login page
|
||||||
if (isConnectionError(error)) {
|
if (isConnectionError(error)) {
|
||||||
handleServerOffline();
|
handleServerOffline();
|
||||||
|
|||||||
@@ -1326,13 +1326,21 @@ export function getLogTypeColors(type: LogEntryType): {
|
|||||||
icon: 'text-primary',
|
icon: 'text-primary',
|
||||||
badge: 'bg-primary/20 text-primary',
|
badge: 'bg-primary/20 text-primary',
|
||||||
};
|
};
|
||||||
|
case 'info':
|
||||||
|
return {
|
||||||
|
bg: 'bg-zinc-500/10',
|
||||||
|
border: 'border-zinc-500/30',
|
||||||
|
text: 'text-primary',
|
||||||
|
icon: 'text-zinc-400',
|
||||||
|
badge: 'bg-zinc-500/20 text-primary',
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
bg: 'bg-zinc-500/10',
|
bg: 'bg-zinc-500/10',
|
||||||
border: 'border-zinc-500/30',
|
border: 'border-zinc-500/30',
|
||||||
text: 'text-zinc-300',
|
text: 'text-black',
|
||||||
icon: 'text-zinc-400',
|
icon: 'text-zinc-400',
|
||||||
badge: 'bg-zinc-500/20 text-zinc-300',
|
badge: 'bg-zinc-500/20 text-black',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user