mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 20:23:36 +00:00
feat: implement pipeline step exclusion functionality
- Added support for excluding specific pipeline steps in feature management, allowing users to skip certain steps during execution. - Introduced a new `PipelineExclusionControls` component for managing exclusions in the UI. - Updated relevant dialogs and components to handle excluded pipeline steps, including `AddFeatureDialog`, `EditFeatureDialog`, and `MassEditDialog`. - Enhanced the `getNextStatus` method in `PipelineService` to account for excluded steps when determining the next status in the pipeline flow. - Updated tests to cover scenarios involving excluded pipeline steps.
This commit is contained in:
@@ -1261,7 +1261,11 @@ export class AutoModeService {
|
||||
|
||||
// Check for pipeline steps and execute them
|
||||
const pipelineConfig = await pipelineService.getPipelineConfig(projectPath);
|
||||
const sortedSteps = [...(pipelineConfig?.steps || [])].sort((a, b) => a.order - b.order);
|
||||
// Filter out excluded pipeline steps and sort by order
|
||||
const excludedStepIds = new Set(feature.excludedPipelineSteps || []);
|
||||
const sortedSteps = [...(pipelineConfig?.steps || [])]
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.filter((step) => !excludedStepIds.has(step.id));
|
||||
|
||||
if (sortedSteps.length > 0) {
|
||||
// Execute pipeline steps sequentially
|
||||
@@ -1723,15 +1727,76 @@ Complete the pipeline step instructions above. Review the previous work and appl
|
||||
): Promise<void> {
|
||||
const featureId = feature.id;
|
||||
|
||||
const sortedSteps = [...pipelineConfig.steps].sort((a, b) => a.order - b.order);
|
||||
// Sort all steps first
|
||||
const allSortedSteps = [...pipelineConfig.steps].sort((a, b) => a.order - b.order);
|
||||
|
||||
// Validate step index
|
||||
if (startFromStepIndex < 0 || startFromStepIndex >= sortedSteps.length) {
|
||||
// Get the current step we're resuming from (using the index from unfiltered list)
|
||||
if (startFromStepIndex < 0 || startFromStepIndex >= allSortedSteps.length) {
|
||||
throw new Error(`Invalid step index: ${startFromStepIndex}`);
|
||||
}
|
||||
const currentStep = allSortedSteps[startFromStepIndex];
|
||||
|
||||
// Get steps to execute (from startFromStepIndex onwards)
|
||||
const stepsToExecute = sortedSteps.slice(startFromStepIndex);
|
||||
// Filter out excluded pipeline steps
|
||||
const excludedStepIds = new Set(feature.excludedPipelineSteps || []);
|
||||
|
||||
// Check if the current step is excluded
|
||||
// If so, use getNextStatus to find the appropriate next step
|
||||
if (excludedStepIds.has(currentStep.id)) {
|
||||
console.log(
|
||||
`[AutoMode] Current step ${currentStep.id} is excluded for feature ${featureId}, finding next valid step`
|
||||
);
|
||||
const nextStatus = pipelineService.getNextStatus(
|
||||
`pipeline_${currentStep.id}`,
|
||||
pipelineConfig,
|
||||
feature.skipTests ?? false,
|
||||
feature.excludedPipelineSteps
|
||||
);
|
||||
|
||||
// If next status is not a pipeline step, feature is done
|
||||
if (!pipelineService.isPipelineStatus(nextStatus)) {
|
||||
await this.updateFeatureStatus(projectPath, featureId, nextStatus);
|
||||
this.emitAutoModeEvent('auto_mode_feature_complete', {
|
||||
featureId,
|
||||
featureName: feature.title,
|
||||
branchName: feature.branchName ?? null,
|
||||
passes: true,
|
||||
message: 'Pipeline completed (remaining steps excluded)',
|
||||
projectPath,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the next step and update the start index
|
||||
const nextStepId = pipelineService.getStepIdFromStatus(nextStatus);
|
||||
const nextStepIndex = allSortedSteps.findIndex((s) => s.id === nextStepId);
|
||||
if (nextStepIndex === -1) {
|
||||
throw new Error(`Next step ${nextStepId} not found in pipeline config`);
|
||||
}
|
||||
startFromStepIndex = nextStepIndex;
|
||||
}
|
||||
|
||||
// Get steps to execute (from startFromStepIndex onwards, excluding excluded steps)
|
||||
const stepsToExecute = allSortedSteps
|
||||
.slice(startFromStepIndex)
|
||||
.filter((step) => !excludedStepIds.has(step.id));
|
||||
|
||||
// If no steps left to execute, complete the feature
|
||||
if (stepsToExecute.length === 0) {
|
||||
const finalStatus = feature.skipTests ? 'waiting_approval' : 'verified';
|
||||
await this.updateFeatureStatus(projectPath, featureId, finalStatus);
|
||||
this.emitAutoModeEvent('auto_mode_feature_complete', {
|
||||
featureId,
|
||||
featureName: feature.title,
|
||||
branchName: feature.branchName ?? null,
|
||||
passes: true,
|
||||
message: 'Pipeline completed (all remaining steps excluded)',
|
||||
projectPath,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the filtered steps for counting
|
||||
const sortedSteps = allSortedSteps.filter((step) => !excludedStepIds.has(step.id));
|
||||
|
||||
console.log(
|
||||
`[AutoMode] Resuming pipeline for feature ${featureId} from step ${startFromStepIndex + 1}/${sortedSteps.length}`
|
||||
|
||||
@@ -234,51 +234,75 @@ export class PipelineService {
|
||||
*
|
||||
* Determines what status a feature should transition to based on current status.
|
||||
* Flow: in_progress -> pipeline_step_0 -> pipeline_step_1 -> ... -> final status
|
||||
* Steps in the excludedStepIds array will be skipped.
|
||||
*
|
||||
* @param currentStatus - Current feature status
|
||||
* @param config - Pipeline configuration (or null if no pipeline)
|
||||
* @param skipTests - Whether to skip tests (affects final status)
|
||||
* @param excludedStepIds - Optional array of step IDs to skip
|
||||
* @returns The next status in the pipeline flow
|
||||
*/
|
||||
getNextStatus(
|
||||
currentStatus: FeatureStatusWithPipeline,
|
||||
config: PipelineConfig | null,
|
||||
skipTests: boolean
|
||||
skipTests: boolean,
|
||||
excludedStepIds?: string[]
|
||||
): FeatureStatusWithPipeline {
|
||||
const steps = config?.steps || [];
|
||||
const exclusions = new Set(excludedStepIds || []);
|
||||
|
||||
// Sort steps by order
|
||||
const sortedSteps = [...steps].sort((a, b) => a.order - b.order);
|
||||
// Sort steps by order and filter out excluded steps
|
||||
const sortedSteps = [...steps]
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.filter((step) => !exclusions.has(step.id));
|
||||
|
||||
// If no pipeline steps, use original logic
|
||||
// If no pipeline steps (or all excluded), use original logic
|
||||
if (sortedSteps.length === 0) {
|
||||
if (currentStatus === 'in_progress') {
|
||||
// If coming from in_progress or already in a pipeline step, go to final status
|
||||
if (currentStatus === 'in_progress' || currentStatus.startsWith('pipeline_')) {
|
||||
return skipTests ? 'waiting_approval' : 'verified';
|
||||
}
|
||||
return currentStatus;
|
||||
}
|
||||
|
||||
// Coming from in_progress -> go to first pipeline step
|
||||
// Coming from in_progress -> go to first non-excluded pipeline step
|
||||
if (currentStatus === 'in_progress') {
|
||||
return `pipeline_${sortedSteps[0].id}`;
|
||||
}
|
||||
|
||||
// Coming from a pipeline step -> go to next step or final status
|
||||
// Coming from a pipeline step -> go to next non-excluded step or final status
|
||||
if (currentStatus.startsWith('pipeline_')) {
|
||||
const currentStepId = currentStatus.replace('pipeline_', '');
|
||||
const currentIndex = sortedSteps.findIndex((s) => s.id === currentStepId);
|
||||
|
||||
if (currentIndex === -1) {
|
||||
// Step not found, go to final status
|
||||
// Current step not found in filtered list (might be excluded or invalid)
|
||||
// Find next valid step after this one from the original sorted list
|
||||
const allSortedSteps = [...steps].sort((a, b) => a.order - b.order);
|
||||
const originalIndex = allSortedSteps.findIndex((s) => s.id === currentStepId);
|
||||
|
||||
if (originalIndex === -1) {
|
||||
// Step truly doesn't exist, go to final status
|
||||
return skipTests ? 'waiting_approval' : 'verified';
|
||||
}
|
||||
|
||||
// Find the next non-excluded step after the current one
|
||||
for (let i = originalIndex + 1; i < allSortedSteps.length; i++) {
|
||||
if (!exclusions.has(allSortedSteps[i].id)) {
|
||||
return `pipeline_${allSortedSteps[i].id}`;
|
||||
}
|
||||
}
|
||||
|
||||
// No more non-excluded steps, go to final status
|
||||
return skipTests ? 'waiting_approval' : 'verified';
|
||||
}
|
||||
|
||||
if (currentIndex < sortedSteps.length - 1) {
|
||||
// Go to next step
|
||||
// Go to next non-excluded step
|
||||
return `pipeline_${sortedSteps[currentIndex + 1].id}`;
|
||||
}
|
||||
|
||||
// Last step completed, go to final status
|
||||
// Last non-excluded step completed, go to final status
|
||||
return skipTests ? 'waiting_approval' : 'verified';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user