fix: in actions_service cancelJobsForRun is bugous use killRun instead (#12366)
The cancelJobsForRun function is redundant with the killRun function and has bugs: - It does not use a transaction and may fail in a non-recoverable way - It does not update the commit status of the run - It does not set NeedRemoval to false if needed Remove the cancelJobsForRun function and use killRun instead (fixing forgejo/forgejo#12386). Both calls are covered by existing tests: - TestCancelPreviousJobs - TestCancelPreviousWithConcurrencyGroup A new integration test TestActionsPullRequestTrustPushCancel is added to verify that the NeedApproval field is set to false whenever a run is cancelled (fixing forgejo/forgejo#12350). Closes forgejo/forgejo#12350 Closes forgejo/forgejo#12386 --- Reverting the change fails the test at https://codeberg.org/limiting-factor/forgejo/src/commit/b6178e5634c230006a3c2b791ec8e06f09367e2d/tests/integration/actions_trust_test.go#L520-L533 with: ``` TAGS='sqlite sqlite_unlock_notify' make 'test-sqlite#TestActionsPullRequestTrustPushCancel' ... actions_trust_test.go:523: Error Trace: /home/limiting-factor/forgejo/tests/integration/actions_trust_test.go:523 /home/limiting-factor/forgejo/tests/integration/git_helper_for_declarative_test.go:98 /home/limiting-factor/forgejo/tests/integration/actions_trust_test.go:476 Error: Should be false Test: TestActionsPullRequestTrustPushCancel ``` ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. All work and communication must conform to Forgejo's [AI Agreement](https://codeberg.org/forgejo/governance/src/branch/main/AIAgreement.md). There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests for Go changes (can be removed for JavaScript changes) - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I ran... - [x] `make pr-go` before pushing ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change. - [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change. <!--start release-notes-assistant--> ## Release notes <!--URL:https://codeberg.org/forgejo/forgejo--> - User Interface bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/12366): <!--number 12366 --><!--line 0 --><!--description V2hlbiB0aGUgYXV0aG9yIG9mIGEgcHVsbCByZXF1ZXN0IGlzIFtkZW5pZWQgdGhlIHJpZ2h0IHRvIHJ1biBBY3Rpb25zXShodHRwczovL2Zvcmdlam8ub3JnL2RvY3MvbmV4dC91c2VyL2FjdGlvbnMvc2VjdXJpdHktcHVsbC1yZXF1ZXN0LykgYnkgY2xpY2tpbmcgb24gdGhlICJEZW55IiBidXR0b24gb24gdGhlIHB1bGwgcmVxdWVzdCB0cnVzdCBtYW5hZ2VtZW50IHBhbmVsLCB0aGUgd29ya2Zsb3cgcnVucyBjcmVhdGVkIGZvciBhbGwgY29tbWl0cyBwdXNoZWQgdG8gdGhlIHB1bGwgcmVxdWVzdCBhcmUgY2FuY2VsbGVkLiBCZWZvcmUgdGhhdCwgcnVucyB0aGF0IHdlcmUgYXV0b21hdGljYWxseSBjYW5jZWxsZWQgYmVjYXVzZSBhIG5ld2VyIGNvbW1pdCB3YXMgcHVzaGVkIHRvIHRoZSBwdWxsIHJlcXVlc3QgW3dlcmUgc3R1Y2sgaW4gYSBzdGF0ZSB3YWl0aW5nIGZvciBhcHByb3ZhbF0oaHR0cHM6Ly9jb2RlYmVyZy5vcmcvZm9yZ2Vqby9mb3JnZWpvL2lzc3Vlcy8xMjM1MCku-->When the author of a pull request is [denied the right to run Actions](https://forgejo.org/docs/next/user/actions/security-pull-request/) by clicking on the "Deny" button on the pull request trust management panel, the workflow runs created for all commits pushed to the pull request are cancelled. Before that, runs that were automatically cancelled because a newer commit was pushed to the pull request [were stuck in a state waiting for approval](https://codeberg.org/forgejo/forgejo/issues/12350).<!--description--> <!--end release-notes-assistant--> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12366 Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org> Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
This commit is contained in:
committed by
Mathieu Fenniak
parent
4e40724199
commit
508bb7f2ae
@@ -23,7 +23,6 @@ import (
|
||||
"code.forgejo.org/forgejo/runner/v12/act/jobparser"
|
||||
act_model "code.forgejo.org/forgejo/runner/v12/act/model"
|
||||
"github.com/gdgvda/cron"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// StartScheduleTasks start the task
|
||||
@@ -220,7 +219,7 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
||||
// Iterate over each found run and cancel its associated jobs.
|
||||
errorSlice := []error{}
|
||||
for _, run := range runs {
|
||||
err := cancelJobsForRun(ctx, run.ID)
|
||||
err := killRun(ctx, run, actions_model.StatusCancelled)
|
||||
errorSlice = append(errorSlice, err)
|
||||
}
|
||||
err = errors.Join(errorSlice...)
|
||||
@@ -236,21 +235,21 @@ func CancelPreviousWithConcurrencyGroup(ctx context.Context, repoID int64, concu
|
||||
// Find all runs in the concurrency group which have at least one job that is still pending; we can't use the run's
|
||||
// status for this because runs are set to failed if a single job is marked as failed, even if other jobs are still
|
||||
// running.
|
||||
runIDs := make([]int64, 0, 10)
|
||||
runs := make([]*actions_model.ActionRun, 0, 10)
|
||||
if err := db.GetEngine(ctx).Table("action_run").
|
||||
Join("INNER", "action_run_job", "action_run_job.run_id = action_run.id").
|
||||
Where("action_run.repo_id = ? AND action_run.concurrency_group = ?", repoID, strings.ToLower(concurrencyGroup)).
|
||||
In("action_run_job.status", actions_model.PendingStatuses()).
|
||||
Distinct("action_run.id").
|
||||
Select("action_run.id").
|
||||
Find(&runIDs); err != nil {
|
||||
Select("action_run.id,action_run.need_approval").
|
||||
Find(&runs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Iterate over each found run and cancel its associated jobs.
|
||||
errorSlice := []error{}
|
||||
for _, runID := range runIDs {
|
||||
err := cancelJobsForRun(ctx, runID)
|
||||
for _, run := range runs {
|
||||
err := killRun(ctx, run, actions_model.StatusCancelled)
|
||||
errorSlice = append(errorSlice, err)
|
||||
}
|
||||
err := errors.Join(errorSlice...)
|
||||
@@ -261,56 +260,6 @@ func CancelPreviousWithConcurrencyGroup(ctx context.Context, repoID int64, concu
|
||||
return nil
|
||||
}
|
||||
|
||||
func cancelJobsForRun(ctx context.Context, runID int64) error {
|
||||
// Find all jobs associated with the current run.
|
||||
jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{
|
||||
RunID: runID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Iterate over each job and attempt to cancel it.
|
||||
errorSlice := []error{}
|
||||
for _, job := range jobs {
|
||||
// Skip jobs that are already in a terminal state (completed, cancelled, etc.).
|
||||
status := job.Status
|
||||
if status.IsDone() {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the job has no associated task (probably an error), set its status to 'Cancelled' and stop it.
|
||||
if job.TaskID == 0 {
|
||||
job.Status = actions_model.StatusCancelled
|
||||
job.Stopped = timeutil.TimeStampNow()
|
||||
|
||||
// Update the job's status and stopped time in the database.
|
||||
n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
|
||||
if err != nil {
|
||||
errorSlice = append(errorSlice, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again.
|
||||
if n == 0 {
|
||||
errorSlice = append(errorSlice, errors.New("job has changed, try again"))
|
||||
continue
|
||||
}
|
||||
|
||||
// Continue with the next job.
|
||||
continue
|
||||
}
|
||||
|
||||
// If the job has an associated task, try to stop the task, effectively cancelling the job.
|
||||
if err := StopTask(ctx, job.TaskID, actions_model.StatusCancelled); err != nil {
|
||||
errorSlice = append(errorSlice, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Join(errorSlice...)
|
||||
}
|
||||
|
||||
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository, cancelPreviousJobs bool) error {
|
||||
// If actions disabled when there is schedule task, this will remove the outdated schedule tasks
|
||||
// There is no other place we can do this because the app.ini will be changed manually
|
||||
|
||||
Reference in New Issue
Block a user