feat: add Check Now button for on-demand bandwidth testing
Some checks failed
Build and Release / Lint (push) Failing after 2m5s
Build and Release / Unit Tests (push) Successful in 2m45s
Build and Release / Create Release (push) Has been skipped
Build and Release / Build Binaries (amd64, darwin) (push) Has been skipped
Build and Release / Build Binaries (amd64, linux) (push) Has been skipped
Build and Release / Build Binaries (amd64, windows) (push) Has been skipped
Build and Release / Build Binaries (arm64, darwin) (push) Has been skipped
Build and Release / Build Binaries (arm64, linux) (push) Has been skipped
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 1m0s

- Add BandwidthTestRequestedAt field to ActionRunner model
- Update to actions-proto-go v0.5.7 with RequestBandwidthTest field
- Add RunnerRequestBandwidthTest handler and route
- Update FetchTask to check and return bandwidth test request flag
- Add Check Now button to runner capabilities panel
- Add locale strings for bandwidth test feature

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
GitCaddy
2026-01-11 15:38:01 +00:00
parent a3c1aa3011
commit e53c8fd040
8 changed files with 82 additions and 7 deletions

2
go.mod
View File

@@ -314,7 +314,7 @@ replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251003180512-a
replace git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078
// Use GitCaddy fork with capability support
replace code.gitea.io/actions-proto-go => git.marketally.com/gitcaddy/actions-proto-go v0.5.6
replace code.gitea.io/actions-proto-go => git.marketally.com/gitcaddy/actions-proto-go v0.5.7
exclude github.com/gofrs/uuid v3.2.0+incompatible

4
go.sum
View File

@@ -29,8 +29,8 @@ dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.marketally.com/gitcaddy/actions-proto-go v0.5.6 h1:G7T0vpx8HyCFWd0YMJ9sp8rCsWtzFrCJK4BMdOFJa1A=
git.marketally.com/gitcaddy/actions-proto-go v0.5.6/go.mod h1:RPu21UoRD3zSAujoZR6LJwuVNa2uFRBveadslczCRfQ=
git.marketally.com/gitcaddy/actions-proto-go v0.5.7 h1:RUbafr3Vkw2l4WfSwa+oF+Ihakbm05W0FlAmXuQrDJc=
git.marketally.com/gitcaddy/actions-proto-go v0.5.7/go.mod h1:RPu21UoRD3zSAujoZR6LJwuVNa2uFRBveadslczCRfQ=
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763 h1:ohdxegvslDEllZmRNDqpKun6L4Oq81jNdEDtGgHEV2c=
gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4=

View File

@@ -64,6 +64,8 @@ type ActionRunner struct {
Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
// CapabilitiesJSON stores structured capability information for AI consumption
CapabilitiesJSON string `xorm:"TEXT"`
// BandwidthTestRequestedAt tracks when a bandwidth test was requested by admin
BandwidthTestRequestedAt timeutil.TimeStamp `xorm:"index"`
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`

View File

@@ -3714,6 +3714,9 @@
"actions.runners.capabilities.disk_warning": "Low disk space",
"actions.runners.capabilities.disk_critical": "Critical: disk almost full",
"actions.runners.capabilities.bandwidth": "Network Bandwidth",
"actions.runners.bandwidth_test_requested": "Bandwidth test requested. Results will appear on next poll.",
"actions.runners.bandwidth_test_request_failed": "Failed to request bandwidth test.",
"actions.runners.check_bandwidth_now": "Check Now",
"actions.runs.all_workflows": "All Workflows",
"actions.runs.commit": "Commit",
"actions.runs.scheduled": "Scheduled",

View File

@@ -177,9 +177,22 @@ func (s *Service) FetchTask(
task = t
}
}
// Check if admin requested a bandwidth test
requestBandwidthTest := false
if runner.BandwidthTestRequestedAt > 0 {
requestBandwidthTest = true
// Clear the request after sending
runner.BandwidthTestRequestedAt = 0
if err := actions_model.UpdateRunner(ctx, runner, "bandwidth_test_requested_at"); err != nil {
log.Warn("failed to clear bandwidth test request: %v", err)
}
}
res := connect.NewResponse(&runnerv1.FetchTaskResponse{
Task: task,
TasksVersion: latestVersion,
Task: task,
TasksVersion: latestVersion,
RequestBandwidthTest: requestBandwidthTest,
})
return res, nil
}

View File

@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
@@ -295,6 +296,45 @@ func ResetRunnerRegistrationToken(ctx *context.Context) {
ctx.JSONRedirect(redirectTo)
}
// RunnerRequestBandwidthTest handles admin request to trigger a bandwidth test
func RunnerRequestBandwidthTest(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)
if err != nil {
ctx.ServerError("getRunnersCtx", err)
return
}
runnerID := ctx.PathParamInt64("runnerid")
ownerID := rCtx.OwnerID
repoID := rCtx.RepoID
redirectTo := rCtx.RedirectLink + url.PathEscape(ctx.PathParam("runnerid"))
runner, err := actions_model.GetRunnerByID(ctx, runnerID)
if err != nil {
log.Warn("RunnerRequestBandwidthTest.GetRunnerByID failed: %v, url: %s", err, ctx.Req.URL)
ctx.ServerError("RunnerRequestBandwidthTest.GetRunnerByID", err)
return
}
if !runner.EditableInContext(ownerID, repoID) {
ctx.NotFound(util.NewPermissionDeniedErrorf("no permission to edit this runner"))
return
}
// Set the bandwidth test request timestamp
runner.BandwidthTestRequestedAt = timeutil.TimeStampNow()
err = actions_model.UpdateRunner(ctx, runner, "bandwidth_test_requested_at")
if err != nil {
log.Warn("RunnerRequestBandwidthTest.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL)
ctx.Flash.Warning(ctx.Tr("actions.runners.bandwidth_test_request_failed"))
ctx.Redirect(redirectTo)
return
}
log.Debug("RunnerRequestBandwidthTest success: %s", ctx.Req.URL)
ctx.Flash.Success(ctx.Tr("actions.runners.bandwidth_test_requested"))
ctx.Redirect(redirectTo)
}
// RunnerDeletePost response for deleting runner
func RunnerDeletePost(ctx *context.Context) {
rCtx, err := getRunnersCtx(ctx)

View File

@@ -510,6 +510,7 @@ func registerWebRoutes(m *web.Router) {
m.Combo("/{runnerid}").Get(shared_actions.RunnersEdit).
Post(web.Bind(forms.EditRunnerForm{}), shared_actions.RunnersEditPost)
m.Post("/{runnerid}/delete", shared_actions.RunnerDeletePost)
m.Post("/{runnerid}/bandwidth-test", shared_actions.RunnerRequestBandwidthTest)
m.Post("/reset_registration_token", shared_actions.ResetRunnerRegistrationToken)
})
}

View File

@@ -115,9 +115,9 @@
</div>
{{end}}
{{if .RunnerCapabilities.Bandwidth}}
<div class="field tw-mb-3">
<label>{{ctx.Locale.Tr "actions.runners.capabilities.bandwidth"}}</label>
{{if .RunnerCapabilities.Bandwidth}}
<div class="tw-flex tw-items-center tw-gap-2">
<span class="ui {{if ge .RunnerCapabilities.Bandwidth.DownloadMbps 100.0}}green{{else if ge .RunnerCapabilities.Bandwidth.DownloadMbps 10.0}}blue{{else}}yellow{{end}} label">
{{svg "octicon-arrow-down" 14}} {{printf "%.1f" .RunnerCapabilities.Bandwidth.DownloadMbps}} Mbps
@@ -127,14 +127,30 @@
{{svg "octicon-clock" 14}} {{printf "%.0f" .RunnerCapabilities.Bandwidth.Latency}} ms
</span>
{{end}}
<form method="post" action="{{.Link}}/bandwidth-test" class="tw-inline">
{{.CsrfTokenHtml}}
<button class="ui tiny basic button" type="submit">
{{svg "octicon-sync" 14}} {{ctx.Locale.Tr "actions.runners.check_bandwidth_now"}}
</button>
</form>
</div>
{{if .RunnerCapabilities.Bandwidth.TestedAt}}
<div class="tw-text-sm tw-text-secondary tw-mt-1">
Tested {{DateUtils.TimeSince .RunnerCapabilities.Bandwidth.TestedAt}}
</div>
{{end}}
{{else}}
<div class="tw-flex tw-items-center tw-gap-2">
<span class="ui basic label">No data yet</span>
<form method="post" action="{{.Link}}/bandwidth-test" class="tw-inline">
{{.CsrfTokenHtml}}
<button class="ui tiny basic button" type="submit">
{{svg "octicon-sync" 14}} {{ctx.Locale.Tr "actions.runners.check_bandwidth_now"}}
</button>
</form>
</div>
{{end}}
</div>
{{end}}
{{if .RunnerCapabilities.Limitations}}
<div class="field tw-mb-3">