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:
Nicolas
2026-03-16 18:24:36 +01:00
committed by GitHub
parent 6372cd7c7d
commit b3b2d111da
27 changed files with 860 additions and 24 deletions

View File

@@ -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 {