Feature: Add per-runner “Disable/Pause” (#36776)
This PR adds per-runner disable/enable support for Gitea Actions so a registered runner can be paused from picking up new jobs without unregistering. Disabled runners stay registered and online but are excluded from new task assignment; running tasks are allowed to finish. Re-enabling restores pickup, and runner list/get responses now expose disabled state. Also added an endpoint for testing http://localhost:3000/devtest/runner-edit/enable <img width="1509" height="701" alt="Bildschirmfoto 2026-02-27 um 22 13 24" src="https://github.com/user-attachments/assets/5328eda9-e59c-46b6-b398-f436e50ee3da" /> Fixes: https://github.com/go-gitea/gitea/issues/36767
This commit is contained in:
@@ -62,6 +62,8 @@ type ActionRunner struct {
|
||||
AgentLabels []string `xorm:"TEXT"`
|
||||
// Store if this is a runner that only ever get one single job assigned
|
||||
Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
|
||||
// Store if this runner is disabled and should not pick up new jobs
|
||||
IsDisabled bool `xorm:"is_disabled NOT NULL DEFAULT false"`
|
||||
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
@@ -199,6 +201,7 @@ type FindRunnerOptions struct {
|
||||
Sort string
|
||||
Filter string
|
||||
IsOnline optional.Option[bool]
|
||||
IsDisabled optional.Option[bool]
|
||||
WithAvailable bool // not only runners belong to, but also runners can be used
|
||||
}
|
||||
|
||||
@@ -239,6 +242,10 @@ func (opts FindRunnerOptions) ToConds() builder.Cond {
|
||||
cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
|
||||
}
|
||||
}
|
||||
|
||||
if opts.IsDisabled.Has() {
|
||||
cond = cond.And(builder.Eq{"is_disabled": opts.IsDisabled.Value()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
@@ -297,6 +304,20 @@ func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func SetRunnerDisabled(ctx context.Context, runner *ActionRunner, isDisabled bool) error {
|
||||
if runner.IsDisabled == isDisabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
runner.IsDisabled = isDisabled
|
||||
if err := UpdateRunner(ctx, runner, "is_disabled"); err != nil {
|
||||
return err
|
||||
}
|
||||
return IncreaseTaskVersion(ctx, runner.OwnerID, runner.RepoID)
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteRunner deletes a runner by given ID.
|
||||
func DeleteRunner(ctx context.Context, id int64) error {
|
||||
if _, err := GetRunnerByID(ctx, id); err != nil {
|
||||
|
||||
@@ -401,6 +401,7 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(324, "Fix closed milestone completeness for milestones with no issues", v1_26.FixClosedMilestoneCompleteness),
|
||||
newMigration(325, "Fix missed repo_id when migrate attachments", v1_26.FixMissedRepoIDWhenMigrateAttachments),
|
||||
newMigration(326, "Migrate commit status target URL to use run ID and job ID", v1_26.FixCommitStatusTargetURLToUseRunAndJobID),
|
||||
newMigration(327, "Add disabled state to action runners", v1_26.AddDisabledToActionRunner),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_26
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func AddDisabledToActionRunner(x *xorm.Engine) error {
|
||||
type ActionRunner struct {
|
||||
IsDisabled bool `xorm:"is_disabled NOT NULL DEFAULT false"`
|
||||
}
|
||||
|
||||
_, err := x.SyncWithOptions(xorm.SyncOptions{
|
||||
IgnoreDropIndices: true,
|
||||
}, new(ActionRunner))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_26
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_AddDisabledToActionRunner(t *testing.T) {
|
||||
type ActionRunner struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Name string
|
||||
}
|
||||
|
||||
x, deferable := base.PrepareTestEnv(t, 0, new(ActionRunner))
|
||||
defer deferable()
|
||||
|
||||
_, err := x.Insert(&ActionRunner{Name: "runner"})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, AddDisabledToActionRunner(x))
|
||||
|
||||
var isDisabled bool
|
||||
has, err := x.SQL("SELECT is_disabled FROM action_runner WHERE id = ?", 1).Get(&isDisabled)
|
||||
require.NoError(t, err)
|
||||
require.True(t, has)
|
||||
require.False(t, isDisabled)
|
||||
}
|
||||
Reference in New Issue
Block a user