Skip to content

Commit

Permalink
fix: Fix cancel in progress to act same pr/branch
Browse files Browse the repository at this point in the history
We need to ensure that we only cancel the pipeline run that is running
on the same branch as the current PR or push event. This ensures that we
don't cancel the wrong pipeline run.

On PullRequest events, we only cancel the pipeline run that is running
on the same Pull Request number.

On Push events, we only cancel the pipeline run that is running on the
same SourceBranch (which is the same as HeadBranch on push)

This avoids the issue when you have multiple pull request running on a
repo and we don't cancel the wrong ones that are running for a different
PR.

Signed-off-by: Chmouel Boudjnah <[email protected]>
  • Loading branch information
chmouel committed Nov 21, 2024
1 parent eb50fac commit 86c8bed
Show file tree
Hide file tree
Showing 4 changed files with 471 additions and 64 deletions.
28 changes: 17 additions & 11 deletions docs/content/docs/guide/running.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,23 @@ You can choose to cancel a PipelineRun that is currently in progress. This can
be done by adding the annotation `pipelinesascode.tekton.dev/cancel-in-progress:
"true"` in the PipelineRun definition.
This feature only works if the PipelineRun is in progress. If the PipelineRun
has already completed or has been cancelled, it will be skipped. (For persistent
settings, refer to the [max-keep-run annotation]({{< relref
"/docs/guide/cleanups.md" >}}) instead.)
The cancellation occurs after the latest PipelineRun has been successfully
created and started. This annotation cannot be used to ensure that only one
PipelineRun is active at any time.
Currently, `cancel-in-progress` cannot be used in conjunction with [concurrency
limit]({{< relref "/docs/guide/repositorycrd.md#concurrency" >}}).
This feature is effective only when the `PipelineRun` is in progress. If the
`PipelineRun` has already completed or been cancelled, the cancellation will
have no effect. (see the [max-keep-run annotation]({{< relref
"/docs/guide/cleanups.md" >}}) instead to clean old `PipelineRuns`.)

The cancellation only applies to `PipelineRuns` within the scope of the current
`PullRequest` or the targeted branch for Push events. For example, if two
`PullRequests` each have a `PipelineRun` with the same name and the
cancel-in-progress annotation, only the `PipelineRun` in the specific PullRequest
will be cancelled. This prevents interference between separate PullRequests.

The cancellation of the older `PipelineRuns` will be executed only after the
latest `PipelineRun` has been created and started successfully. This annotation
cannot guarantee that only one `PipelineRun` will be active at any given time.

Currently, `cancel-in-progress` cannot be used in conjunction with the [concurrency
limit]({{< relref "/docs/guide/repositorycrd.md#concurrency" >}}) setting.

### Cancelling a PipelineRun with a GitOps command

Expand Down
44 changes: 31 additions & 13 deletions pkg/pipelineascode/cancel_pipelineruns.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import (
"strconv"
"sync"

"github.com/openshift-pipelines/pipelines-as-code/pkg/action"
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
"github.com/openshift-pipelines/pipelines-as-code/pkg/formatting"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/triggertype"
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"

"github.com/openshift-pipelines/pipelines-as-code/pkg/action"
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
"github.com/openshift-pipelines/pipelines-as-code/pkg/formatting"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/triggertype"
)

var cancelMergePatch = map[string]interface{}{
Expand All @@ -38,21 +39,27 @@ func (p *PacRun) cancelInProgress(ctx context.Context, matchPR *tektonv1.Pipelin
return nil
}

if repo.Spec.ConcurrencyLimit != nil && *repo.Spec.ConcurrencyLimit > 0 {
return fmt.Errorf("cancel in progress is not supported with concurrency limit")
}

prName, ok := matchPR.GetAnnotations()[keys.OriginalPRName]
if !ok {
return nil
}
labelSelector := getLabelSelector(map[string]string{

if repo.Spec.ConcurrencyLimit != nil && *repo.Spec.ConcurrencyLimit > 0 {
return fmt.Errorf("cancel in progress is not supported with concurrency limit")
}

labelMap := map[string]string{
keys.URLRepository: formatting.CleanValueKubernetes(p.event.Repository),
keys.OriginalPRName: prName,
})

keys.EventType: p.event.TriggerTarget.String(),
}
if p.event.TriggerTarget == triggertype.PullRequest {
labelMap[keys.PullRequest] = strconv.Itoa(p.event.PullRequestNumber)
}
labelMaps := getLabelSelector(labelMap)
p.run.Clients.Log.Infof("cancel-in-progress: selecting pipelineRuns to cancel with labels: %v", labelMaps)
prs, err := p.run.Clients.Tekton.TektonV1().PipelineRuns(matchPR.GetNamespace()).List(ctx, metav1.ListOptions{
LabelSelector: labelSelector,
LabelSelector: labelMaps,
})
if err != nil {
return fmt.Errorf("failed to list pipelineRuns : %w", err)
Expand All @@ -62,6 +69,17 @@ func (p *PacRun) cancelInProgress(ctx context.Context, matchPR *tektonv1.Pipelin
if pr.GetName() == matchPR.GetName() {
continue
}
if sourceBranch, ok := pr.GetAnnotations()[keys.SourceBranch]; ok {
// NOTE(chmouel): Every PR has their own branch and so is every push to different branch
// it means we only cancel pipelinerun of the same name that runs to
// the unique branch. Note: HeadBranch is the branch from where the PR
// comes from in git jargon.
if sourceBranch != p.event.HeadBranch {
p.logger.Infof("cancel-in-progress: skipping pipelinerun %v/%v as it is not from the same branch, annotation source-branch: %s event headbranch: %s", pr.GetNamespace(), pr.GetName(), sourceBranch, p.event.HeadBranch)
continue
}
}

if pr.IsDone() {
continue
}
Expand Down
Loading

0 comments on commit 86c8bed

Please sign in to comment.