@@ -84,6 +84,7 @@ type HandlerService interface {
8484 GetRunningWorkflowLogs (workflowId int , followLogs bool ) (* bufio.Reader , func () error , error )
8585 GetHistoricBuildLogs (workflowId int , ciWorkflow * pipelineConfig.CiWorkflow ) (map [string ]string , error )
8686 DownloadCiWorkflowArtifacts (pipelineId int , buildId int ) (* os.File , error )
87+ abortPreviousRunningBuilds (pipelineId int , triggeredBy int32 ) error
8788}
8889
8990// CATEGORY=CI_BUILDX
@@ -707,6 +708,13 @@ func (impl *HandlerServiceImpl) triggerCiPipeline(trigger *types.CiTriggerReques
707708 return 0 , err
708709 }
709710
711+ // Check if auto-abort is enabled for this pipeline and abort previous builds if needed
712+ err = impl .abortPreviousRunningBuilds (trigger .PipelineId , trigger .TriggeredBy )
713+ if err != nil {
714+ impl .Logger .Errorw ("error in aborting previous running builds" , "pipelineId" , trigger .PipelineId , "err" , err )
715+ // Log error but don't fail the trigger - previous builds aborting is a best-effort operation
716+ }
717+
710718 err = impl .executeCiPipeline (workflowRequest )
711719 if err != nil {
712720 impl .Logger .Errorw ("error in executing ci pipeline" , "err" , err )
@@ -2065,3 +2073,84 @@ func (impl *HandlerServiceImpl) DownloadCiWorkflowArtifacts(pipelineId int, buil
20652073 impl .Logger .Infow ("Downloaded " , "filename" , file .Name (), "bytes" , numBytes )
20662074 return file , nil
20672075}
2076+
2077+ // abortPreviousRunningBuilds checks if auto-abort is enabled for the pipeline and aborts previous running builds
2078+ func (impl * HandlerServiceImpl ) abortPreviousRunningBuilds (pipelineId int , triggeredBy int32 ) error {
2079+ // Get pipeline configuration to check if auto-abort is enabled
2080+ ciPipeline , err := impl .ciPipelineRepository .FindById (pipelineId )
2081+ if err != nil {
2082+ impl .Logger .Errorw ("error in finding ci pipeline" , "pipelineId" , pipelineId , "err" , err )
2083+ return err
2084+ }
2085+
2086+ // Check if auto-abort is enabled for this pipeline
2087+ if ! ciPipeline .AutoAbortPreviousBuilds {
2088+ impl .Logger .Debugw ("auto-abort not enabled for pipeline" , "pipelineId" , pipelineId )
2089+ return nil
2090+ }
2091+
2092+ // Find all running/pending workflows for this pipeline
2093+ runningWorkflows , err := impl .ciWorkflowRepository .FindRunningWorkflowsForPipeline (pipelineId )
2094+ if err != nil {
2095+ impl .Logger .Errorw ("error in finding running workflows for pipeline" , "pipelineId" , pipelineId , "err" , err )
2096+ return err
2097+ }
2098+
2099+ if len (runningWorkflows ) == 0 {
2100+ impl .Logger .Debugw ("no running workflows found to abort for pipeline" , "pipelineId" , pipelineId )
2101+ return nil
2102+ }
2103+
2104+ impl .Logger .Infow ("found running workflows to abort due to auto-abort configuration" ,
2105+ "pipelineId" , pipelineId , "workflowCount" , len (runningWorkflows ), "triggeredBy" , triggeredBy )
2106+
2107+ // Abort each running workflow
2108+ for _ , workflow := range runningWorkflows {
2109+ // Check if the workflow is in a critical phase that should not be aborted
2110+ if impl .isWorkflowInCriticalPhase (workflow ) {
2111+ impl .Logger .Infow ("skipping abort of workflow in critical phase" ,
2112+ "workflowId" , workflow .Id , "status" , workflow .Status , "pipelineId" , pipelineId )
2113+ continue
2114+ }
2115+
2116+ // Attempt to cancel the build
2117+ _ , err := impl .CancelBuild (workflow .Id , false )
2118+ if err != nil {
2119+ impl .Logger .Errorw ("error aborting previous running build" ,
2120+ "workflowId" , workflow .Id , "pipelineId" , pipelineId , "err" , err )
2121+ // Continue with other workflows even if one fails
2122+ continue
2123+ }
2124+
2125+ impl .Logger .Infow ("successfully aborted previous running build due to auto-abort" ,
2126+ "workflowId" , workflow .Id , "pipelineId" , pipelineId , "abortedBy" , triggeredBy )
2127+ }
2128+
2129+ return nil
2130+ }
2131+
2132+ // isWorkflowInCriticalPhase determines if a workflow is in a critical phase and should not be aborted
2133+ // This protects builds that are in the final stages like pushing cache or artifacts
2134+ func (impl * HandlerServiceImpl ) isWorkflowInCriticalPhase (workflow * pipelineConfig.CiWorkflow ) bool {
2135+ // For now, we consider "Starting" as safe to abort, but "Running" needs more careful consideration
2136+ // In the future, this could be extended to check actual workflow steps/stages
2137+
2138+ // If workflow has been running for less than 2 minutes, it's likely still in setup phase
2139+ if workflow .Status == "Running" && workflow .StartedOn .IsZero () == false {
2140+ runningDuration := time .Since (workflow .StartedOn )
2141+ if runningDuration < 2 * time .Minute {
2142+ impl .Logger .Debugw ("workflow is in early running phase, safe to abort" ,
2143+ "workflowId" , workflow .Id , "runningDuration" , runningDuration .String ())
2144+ return false
2145+ }
2146+
2147+ // For workflows running longer, we should be more cautious
2148+ // This could be extended to check actual workflow phases using workflow service APIs
2149+ impl .Logger .Debugw ("workflow has been running for a while, considering as critical phase" ,
2150+ "workflowId" , workflow .Id , "runningDuration" , runningDuration .String ())
2151+ return true
2152+ }
2153+
2154+ // "Starting" and "Pending" are generally safe to abort
2155+ return false
2156+ }
0 commit comments