feat(runners): Add suggested labels and label management
Some checks failed
Build and Release / Build Binaries (arm64, linux) (push) Has been skipped
Build and Release / Create Release (push) Has been skipped
Build and Release / Lint (push) Failing after 3s
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 / Integration Tests (PostgreSQL) (push) Successful in 51s
Build and Release / Unit Tests (push) Has been cancelled
Some checks failed
Build and Release / Build Binaries (arm64, linux) (push) Has been skipped
Build and Release / Create Release (push) Has been skipped
Build and Release / Lint (push) Failing after 3s
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 / Integration Tests (PostgreSQL) (push) Successful in 51s
Build and Release / Unit Tests (push) Has been cancelled
- Add DistroInfo struct to parse Linux distribution from capabilities
- Add runner label management endpoints (add/remove/use-suggested)
- Update runner edit UI with:
- Clickable labels with X to remove
- Suggested labels with + to add individually
- Use All Suggested Labels button
- Buttons moved to full-width row below columns
- Suggested labels derived from OS and distro (linux, linux-latest, debian, debian-latest, etc)
🤖 Generated with Claude Code
This commit is contained in:
@@ -21,10 +21,18 @@ type DiskInfo struct {
|
||||
UsedPercent float64 `json:"used_percent"`
|
||||
}
|
||||
|
||||
// DistroInfo holds Linux distribution information
|
||||
type DistroInfo struct {
|
||||
ID string `json:"id,omitempty"` // e.g., "ubuntu", "debian", "fedora"
|
||||
VersionID string `json:"version_id,omitempty"` // e.g., "24.04", "12"
|
||||
PrettyName string `json:"pretty_name,omitempty"` // e.g., "Ubuntu 24.04 LTS"
|
||||
}
|
||||
|
||||
// RunnerCapability represents the detailed capabilities of a runner
|
||||
type RunnerCapability struct {
|
||||
OS string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
Distro *DistroInfo `json:"distro,omitempty"`
|
||||
Docker bool `json:"docker"`
|
||||
DockerCompose bool `json:"docker_compose"`
|
||||
ContainerRuntime string `json:"container_runtime,omitempty"`
|
||||
@@ -34,6 +42,7 @@ type RunnerCapability struct {
|
||||
Limitations []string `json:"limitations,omitempty"`
|
||||
Disk *DiskInfo `json:"disk,omitempty"`
|
||||
Bandwidth *BandwidthInfo `json:"bandwidth,omitempty"`
|
||||
SuggestedLabels []string `json:"suggested_labels,omitempty"`
|
||||
}
|
||||
|
||||
// CapabilityFeatures represents feature support flags
|
||||
|
||||
@@ -3716,7 +3716,7 @@
|
||||
"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.runners.check_bandwidth_now": "Check Bandwidth",
|
||||
"actions.runs.all_workflows": "All Workflows",
|
||||
"actions.runs.commit": "Commit",
|
||||
"actions.runs.scheduled": "Scheduled",
|
||||
|
||||
@@ -5,6 +5,7 @@ package actions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
@@ -408,3 +409,174 @@ func findActionsRunner(ctx *context.Context, rCtx *runnersCtx) *actions_model.Ac
|
||||
|
||||
return got[0]
|
||||
}
|
||||
// RunnerAddLabel adds a single label to a runner
|
||||
func RunnerAddLabel(ctx *context.Context) {
|
||||
rCtx, err := getRunnersCtx(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("getRunnersCtx", err)
|
||||
return
|
||||
}
|
||||
|
||||
runner := findActionsRunner(ctx, rCtx)
|
||||
if runner == nil {
|
||||
return
|
||||
}
|
||||
|
||||
label := ctx.FormString("label")
|
||||
if label == "" {
|
||||
ctx.Flash.Warning("No label specified")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
// Check if label already exists
|
||||
for _, existing := range runner.AgentLabels {
|
||||
if existing == label {
|
||||
ctx.Flash.Info("Label already exists")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Add the label
|
||||
runner.AgentLabels = append(runner.AgentLabels, label)
|
||||
|
||||
err = actions_model.UpdateRunner(ctx, runner, "agent_labels")
|
||||
if err != nil {
|
||||
log.Warn("RunnerAddLabel.UpdateRunner failed: %v", err)
|
||||
ctx.Flash.Warning("Failed to add label")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success("Label added: " + label)
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
}
|
||||
|
||||
// RunnerRemoveLabel removes a single label from a runner
|
||||
func RunnerRemoveLabel(ctx *context.Context) {
|
||||
rCtx, err := getRunnersCtx(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("getRunnersCtx", err)
|
||||
return
|
||||
}
|
||||
|
||||
runner := findActionsRunner(ctx, rCtx)
|
||||
if runner == nil {
|
||||
return
|
||||
}
|
||||
|
||||
label := ctx.FormString("label")
|
||||
if label == "" {
|
||||
ctx.Flash.Warning("No label specified")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
// Remove the label
|
||||
newLabels := make([]string, 0, len(runner.AgentLabels))
|
||||
found := false
|
||||
for _, existing := range runner.AgentLabels {
|
||||
if existing == label {
|
||||
found = true
|
||||
} else {
|
||||
newLabels = append(newLabels, existing)
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
ctx.Flash.Info("Label not found")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
runner.AgentLabels = newLabels
|
||||
|
||||
err = actions_model.UpdateRunner(ctx, runner, "agent_labels")
|
||||
if err != nil {
|
||||
log.Warn("RunnerRemoveLabel.UpdateRunner failed: %v", err)
|
||||
ctx.Flash.Warning("Failed to remove label")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success("Label removed: " + label)
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
}
|
||||
|
||||
// RunnerUseSuggestedLabels adds all suggested labels based on capabilities
|
||||
func RunnerUseSuggestedLabels(ctx *context.Context) {
|
||||
rCtx, err := getRunnersCtx(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("getRunnersCtx", err)
|
||||
return
|
||||
}
|
||||
|
||||
runner := findActionsRunner(ctx, rCtx)
|
||||
if runner == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse capabilities to get suggested labels
|
||||
if runner.CapabilitiesJSON == "" {
|
||||
ctx.Flash.Warning("No capabilities data available")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
var caps structs.RunnerCapability
|
||||
if err := json.Unmarshal([]byte(runner.CapabilitiesJSON), &caps); err != nil {
|
||||
ctx.Flash.Warning("Failed to parse capabilities")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
// Build suggested labels
|
||||
suggestedLabels := []string{}
|
||||
existingSet := make(map[string]bool)
|
||||
for _, label := range runner.AgentLabels {
|
||||
existingSet[label] = true
|
||||
}
|
||||
|
||||
// OS-based labels
|
||||
switch caps.OS {
|
||||
case "linux":
|
||||
suggestedLabels = append(suggestedLabels, "linux", "linux-latest")
|
||||
case "windows":
|
||||
suggestedLabels = append(suggestedLabels, "windows", "windows-latest")
|
||||
case "darwin":
|
||||
suggestedLabels = append(suggestedLabels, "macos", "macos-latest")
|
||||
}
|
||||
|
||||
// Distro-based labels
|
||||
if caps.Distro != nil && caps.Distro.ID != "" {
|
||||
suggestedLabels = append(suggestedLabels, caps.Distro.ID, caps.Distro.ID+"-latest")
|
||||
}
|
||||
|
||||
// Add only new labels
|
||||
added := []string{}
|
||||
for _, label := range suggestedLabels {
|
||||
if !existingSet[label] {
|
||||
runner.AgentLabels = append(runner.AgentLabels, label)
|
||||
added = append(added, label)
|
||||
existingSet[label] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(added) == 0 {
|
||||
ctx.Flash.Info("All suggested labels already exist")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
err = actions_model.UpdateRunner(ctx, runner, "agent_labels")
|
||||
if err != nil {
|
||||
log.Warn("RunnerUseSuggestedLabels.UpdateRunner failed: %v", err)
|
||||
ctx.Flash.Warning("Failed to add labels")
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success("Added labels: " + strings.Join(added, ", "))
|
||||
ctx.Redirect(rCtx.RedirectLink + ctx.PathParam("runnerid"))
|
||||
}
|
||||
|
||||
@@ -511,6 +511,9 @@ func registerWebRoutes(m *web.Router) {
|
||||
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("/{runnerid}/add-label", shared_actions.RunnerAddLabel)
|
||||
m.Post("/{runnerid}/remove-label", shared_actions.RunnerRemoveLabel)
|
||||
m.Post("/{runnerid}/use-suggested-labels", shared_actions.RunnerUseSuggestedLabels)
|
||||
m.Post("/reset_registration_token", shared_actions.ResetRunnerRegistrationToken)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,47 +3,142 @@
|
||||
{{ctx.Locale.Tr "actions.runners.runner_title"}} {{.Runner.ID}} {{.Runner.Name}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<div class="ui two column stackable grid">
|
||||
<!-- Left Column: Basic Info & Controls -->
|
||||
<!-- Health Status Tiles -->
|
||||
<div class="ui three column stackable grid tw-mb-4">
|
||||
<div class="column">
|
||||
<div class="ui segment tw-text-center">
|
||||
<div class="tw-text-sm tw-mb-1" style="opacity: 0.8;">Status</div>
|
||||
<span class="ui {{if .Runner.IsOnline}}green{{else}}red{{end}} large label">
|
||||
{{if .Runner.IsOnline}}{{svg "octicon-check-circle" 16}}{{else}}{{svg "octicon-x-circle" 16}}{{end}}
|
||||
{{.Runner.StatusLocaleName ctx.Locale}}
|
||||
</span>
|
||||
<div class="tw-text-xs tw-mt-2" style="opacity: 0.7;">
|
||||
{{if .Runner.LastOnline}}Last seen {{DateUtils.TimeSince .Runner.LastOnline}}{{else}}Never connected{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="ui segment tw-text-center">
|
||||
<div class="tw-text-sm tw-mb-1" style="opacity: 0.8;">Disk Space</div>
|
||||
{{if and .RunnerCapabilities .RunnerCapabilities.Disk}}
|
||||
{{$diskUsed := .RunnerCapabilities.Disk.UsedPercent}}
|
||||
{{$diskFreeGB := DivideFloat64 (Int64ToFloat64 .RunnerCapabilities.Disk.Free) 1073741824.0}}
|
||||
{{$diskTotalGB := DivideFloat64 (Int64ToFloat64 .RunnerCapabilities.Disk.Total) 1073741824.0}}
|
||||
<span class="ui {{if ge $diskUsed 95.0}}red{{else if ge $diskUsed 85.0}}yellow{{else}}green{{end}} large label">
|
||||
{{if ge $diskUsed 95.0}}{{svg "octicon-alert" 16}}{{else if ge $diskUsed 85.0}}{{svg "octicon-alert" 16}}{{else}}{{svg "octicon-database" 16}}{{end}}
|
||||
{{printf "%.0f" $diskUsed}}% used
|
||||
</span>
|
||||
<div class="tw-text-xs tw-mt-2" style="opacity: 0.7;">
|
||||
{{printf "%.1f" $diskFreeGB}} GB free of {{printf "%.0f" $diskTotalGB}} GB
|
||||
</div>
|
||||
{{else}}
|
||||
<span class="ui grey large label">{{svg "octicon-database" 16}} No data</span>
|
||||
<div class="tw-text-xs tw-mt-2" style="opacity: 0.7;">Waiting for report</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="ui segment tw-text-center">
|
||||
<div class="tw-text-sm tw-mb-1" style="opacity: 0.8;">Network</div>
|
||||
{{if and .RunnerCapabilities .RunnerCapabilities.Bandwidth}}
|
||||
<span class="ui {{if ge .RunnerCapabilities.Bandwidth.DownloadMbps 100.0}}green{{else if ge .RunnerCapabilities.Bandwidth.DownloadMbps 10.0}}blue{{else}}yellow{{end}} large label">
|
||||
{{svg "octicon-arrow-down" 16}} {{printf "%.0f" .RunnerCapabilities.Bandwidth.DownloadMbps}} Mbps
|
||||
</span>
|
||||
<div class="tw-text-xs tw-mt-2" style="opacity: 0.7;">
|
||||
{{if gt .RunnerCapabilities.Bandwidth.Latency 0.0}}{{printf "%.0f" .RunnerCapabilities.Bandwidth.Latency}} ms latency{{end}}
|
||||
{{if .RunnerCapabilities.Bandwidth.TestedAt}}- tested {{DateUtils.TimeSince .RunnerCapabilities.Bandwidth.TestedAt}}{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<span class="ui grey large label">{{svg "octicon-globe" 16}} No data</span>
|
||||
<div class="tw-text-xs tw-mt-2" style="opacity: 0.7;">Waiting for test</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui two column stackable grid">
|
||||
<!-- Left Column: Runner Info & Controls -->
|
||||
<div class="column">
|
||||
<div class="ui segment">
|
||||
<h5 class="ui header">Runner Information</h5>
|
||||
<table class="ui very basic table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 100px; opacity: 0.8;">Version</td>
|
||||
<td><span class="ui small blue label">{{.Runner.Version}}</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="opacity: 0.8;">Owner</td>
|
||||
<td data-tooltip-content="{{.Runner.BelongsToOwnerName}}">{{.Runner.BelongsToOwnerType.LocaleString ctx.Locale}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="opacity: 0.8;">Labels</td>
|
||||
<td>
|
||||
{{range .Runner.AgentLabels}}
|
||||
<form method="post" action="{{$.Link}}/remove-label" style="display:inline;">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input type="hidden" name="label" value="{{.}}">
|
||||
<button type="submit" class="ui small blue label tw-my-1" style="cursor:pointer;">{{.}} {{svg "octicon-x" 12}}</button>
|
||||
</form>
|
||||
{{end}}
|
||||
{{if not .Runner.AgentLabels}}<span style="opacity: 0.6;">No labels</span>{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Suggested Labels Section -->
|
||||
{{if .RunnerCapabilities}}
|
||||
<div class="ui segment">
|
||||
<h5 class="ui header">{{svg "octicon-light-bulb" 16}} Suggested Labels</h5>
|
||||
<p class="tw-text-sm tw-mb-2" style="opacity: 0.7;">Based on detected capabilities. Click + to add individually.</p>
|
||||
<div class="tw-flex tw-flex-wrap tw-gap-2" id="suggested-labels">
|
||||
{{$labels := .Runner.AgentLabels}}
|
||||
{{if eq .RunnerCapabilities.OS "linux"}}
|
||||
{{if not (SliceUtils.Contains $labels "linux")}}
|
||||
<form method="post" action="{{$.Link}}/add-label" style="display:inline;">{{$.CsrfTokenHtml}}<input type="hidden" name="label" value="linux"><button type="submit" class="ui small teal label" style="cursor:pointer;">{{svg "octicon-plus" 12}} linux</button></form>
|
||||
{{else}}<span class="ui small teal label">linux</span>{{end}}
|
||||
{{if not (SliceUtils.Contains $labels "linux-latest")}}
|
||||
<form method="post" action="{{$.Link}}/add-label" style="display:inline;">{{$.CsrfTokenHtml}}<input type="hidden" name="label" value="linux-latest"><button type="submit" class="ui small teal label" style="cursor:pointer;">{{svg "octicon-plus" 12}} linux-latest</button></form>
|
||||
{{else}}<span class="ui small teal label">linux-latest</span>{{end}}
|
||||
{{else if eq .RunnerCapabilities.OS "windows"}}
|
||||
{{if not (SliceUtils.Contains $labels "windows")}}
|
||||
<form method="post" action="{{$.Link}}/add-label" style="display:inline;">{{$.CsrfTokenHtml}}<input type="hidden" name="label" value="windows"><button type="submit" class="ui small teal label" style="cursor:pointer;">{{svg "octicon-plus" 12}} windows</button></form>
|
||||
{{else}}<span class="ui small teal label">windows</span>{{end}}
|
||||
{{if not (SliceUtils.Contains $labels "windows-latest")}}
|
||||
<form method="post" action="{{$.Link}}/add-label" style="display:inline;">{{$.CsrfTokenHtml}}<input type="hidden" name="label" value="windows-latest"><button type="submit" class="ui small teal label" style="cursor:pointer;">{{svg "octicon-plus" 12}} windows-latest</button></form>
|
||||
{{else}}<span class="ui small teal label">windows-latest</span>{{end}}
|
||||
{{else if eq .RunnerCapabilities.OS "darwin"}}
|
||||
{{if not (SliceUtils.Contains $labels "macos")}}
|
||||
<form method="post" action="{{$.Link}}/add-label" style="display:inline;">{{$.CsrfTokenHtml}}<input type="hidden" name="label" value="macos"><button type="submit" class="ui small teal label" style="cursor:pointer;">{{svg "octicon-plus" 12}} macos</button></form>
|
||||
{{else}}<span class="ui small teal label">macos</span>{{end}}
|
||||
{{if not (SliceUtils.Contains $labels "macos-latest")}}
|
||||
<form method="post" action="{{$.Link}}/add-label" style="display:inline;">{{$.CsrfTokenHtml}}<input type="hidden" name="label" value="macos-latest"><button type="submit" class="ui small teal label" style="cursor:pointer;">{{svg "octicon-plus" 12}} macos-latest</button></form>
|
||||
{{else}}<span class="ui small teal label">macos-latest</span>{{end}}
|
||||
{{end}}
|
||||
{{if and .RunnerCapabilities.Distro .RunnerCapabilities.Distro.ID}}
|
||||
{{$distro := .RunnerCapabilities.Distro.ID}}
|
||||
{{$distroLatest := printf "%s-latest" .RunnerCapabilities.Distro.ID}}
|
||||
{{if not (SliceUtils.Contains $labels $distro)}}
|
||||
<form method="post" action="{{$.Link}}/add-label" style="display:inline;">{{$.CsrfTokenHtml}}<input type="hidden" name="label" value="{{$distro}}"><button type="submit" class="ui small purple label" style="cursor:pointer;">{{svg "octicon-plus" 12}} {{$distro}}</button></form>
|
||||
{{else}}<span class="ui small purple label">{{$distro}}</span>{{end}}
|
||||
{{if not (SliceUtils.Contains $labels $distroLatest)}}
|
||||
<form method="post" action="{{$.Link}}/add-label" style="display:inline;">{{$.CsrfTokenHtml}}<input type="hidden" name="label" value="{{$distroLatest}}"><button type="submit" class="ui small purple label" style="cursor:pointer;">{{svg "octicon-plus" 12}} {{$distroLatest}}</button></form>
|
||||
{{else}}<span class="ui small purple label">{{$distroLatest}}</span>{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<form class="ui form" method="post">
|
||||
{{template "base/disable_form_autofill"}}
|
||||
<div class="runner-basic-info">
|
||||
<div class="ui segment">
|
||||
<h5 class="ui header">AI Instructions</h5>
|
||||
<p class="tw-text-sm tw-mb-2" style="opacity: 0.7;">Additional context for AI when selecting this runner for jobs.</p>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "actions.runners.status"}}</label>
|
||||
<span class="ui {{if .Runner.IsOnline}}green{{else}}basic{{end}} label">{{.Runner.StatusLocaleName ctx.Locale}}</span>
|
||||
<textarea id="description" name="description" rows="3" placeholder="e.g., Use for heavy builds, has GPU, limited to 2 concurrent jobs...">{{.Runner.Description}}</textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "actions.runners.last_online"}}</label>
|
||||
<span>{{if .Runner.LastOnline}}{{DateUtils.TimeSince .Runner.LastOnline}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "actions.runners.labels"}}</label>
|
||||
<span>
|
||||
{{range .Runner.AgentLabels}}
|
||||
<span class="ui label">{{.}}</span>
|
||||
{{end}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "actions.runners.owner_type"}}</label>
|
||||
<span data-tooltip-content="{{.Runner.BelongsToOwnerName}}">{{.Runner.BelongsToOwnerType.LocaleString ctx.Locale}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="field">
|
||||
<label for="description">{{ctx.Locale.Tr "actions.runners.description"}}</label>
|
||||
<input id="description" name="description" value="{{.Runner.Description}}">
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui primary button" data-url="{{.Link}}">{{ctx.Locale.Tr "actions.runners.update_runner"}}</button>
|
||||
<button class="ui red button delete-button" data-url="{{.Link}}/delete" data-modal="#runner-delete-modal">
|
||||
{{ctx.Locale.Tr "actions.runners.delete_runner"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -57,16 +152,19 @@
|
||||
{{if .RunnerCapabilities.OS}}
|
||||
<div class="field tw-mb-3">
|
||||
<label>{{ctx.Locale.Tr "actions.runners.capabilities.os"}}</label>
|
||||
<span class="ui label">{{.RunnerCapabilities.OS}}/{{.RunnerCapabilities.Arch}}</span>
|
||||
<span class="ui small blue label">{{.RunnerCapabilities.OS}}/{{.RunnerCapabilities.Arch}}</span>
|
||||
{{if and .RunnerCapabilities.Distro .RunnerCapabilities.Distro.PrettyName}}
|
||||
<span class="ui small label">{{.RunnerCapabilities.Distro.PrettyName}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="field tw-mb-3">
|
||||
<label>{{ctx.Locale.Tr "actions.runners.capabilities.docker"}}</label>
|
||||
{{if .RunnerCapabilities.Docker}}
|
||||
<span class="ui green label">{{svg "octicon-check" 14}} {{ctx.Locale.Tr "actions.runners.capabilities.available"}}</span>
|
||||
<span class="ui small green label">{{svg "octicon-check" 14}} Available</span>
|
||||
{{else}}
|
||||
<span class="ui basic label">{{svg "octicon-x" 14}} Not available</span>
|
||||
<span class="ui small orange label">{{svg "octicon-x" 14}} Not available</span>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -75,7 +173,7 @@
|
||||
<label>{{ctx.Locale.Tr "actions.runners.capabilities.shells"}}</label>
|
||||
<div>
|
||||
{{range .RunnerCapabilities.Shell}}
|
||||
<span class="ui label">{{.}}</span>
|
||||
<span class="ui small teal label tw-mr-1">{{.}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,72 +184,12 @@
|
||||
<label>{{ctx.Locale.Tr "actions.runners.capabilities.tools"}}</label>
|
||||
<div class="tw-flex tw-flex-wrap tw-gap-1">
|
||||
{{range $tool, $versions := .RunnerCapabilities.Tools}}
|
||||
<span class="ui small label">{{$tool}} {{range $versions}}{{.}} {{end}}</span>
|
||||
<span class="ui small purple label">{{$tool}} {{range $versions}}{{.}} {{end}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .RunnerCapabilities.Disk}}
|
||||
<div class="field tw-mb-3">
|
||||
<label>{{ctx.Locale.Tr "actions.runners.capabilities.disk"}}</label>
|
||||
{{$diskUsed := .RunnerCapabilities.Disk.UsedPercent}}
|
||||
{{$diskFreeGB := DivideFloat64 (Int64ToFloat64 .RunnerCapabilities.Disk.Free) 1073741824.0}}
|
||||
{{$diskTotalGB := DivideFloat64 (Int64ToFloat64 .RunnerCapabilities.Disk.Total) 1073741824.0}}
|
||||
{{$diskUsedInt := printf "%.0f" $diskUsed}}
|
||||
<div class="ui small progress {{if ge $diskUsed 95.0}}red{{else if ge $diskUsed 85.0}}yellow{{else}}green{{end}}" data-percent="{{$diskUsedInt}}" style="margin-bottom: 0.5em;">
|
||||
<div class="bar" style="width: {{$diskUsedInt}}%;">
|
||||
<div class="progress">{{printf "%.1f" $diskUsed}}%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-text-sm tw-text-secondary">
|
||||
{{printf "%.1f" $diskFreeGB}} GB free / {{printf "%.1f" $diskTotalGB}} GB total
|
||||
{{if ge $diskUsed 95.0}}
|
||||
<span class="ui red text tw-ml-2">{{svg "octicon-alert" 14}}</span>
|
||||
{{else if ge $diskUsed 85.0}}
|
||||
<span class="ui yellow text tw-ml-2">{{svg "octicon-alert" 14}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<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
|
||||
</span>
|
||||
{{if gt .RunnerCapabilities.Bandwidth.Latency 0.0}}
|
||||
<span class="ui {{if le .RunnerCapabilities.Bandwidth.Latency 50.0}}green{{else if le .RunnerCapabilities.Bandwidth.Latency 150.0}}yellow{{else}}red{{end}} label">
|
||||
{{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>
|
||||
|
||||
{{if .RunnerCapabilities.Limitations}}
|
||||
<div class="field tw-mb-3">
|
||||
<label>{{ctx.Locale.Tr "actions.runners.capabilities.limitations"}}</label>
|
||||
@@ -169,11 +207,37 @@
|
||||
{{else}}
|
||||
<div class="ui segment">
|
||||
<h5 class="ui header">{{ctx.Locale.Tr "actions.runners.capabilities"}}</h5>
|
||||
<p class="tw-text-secondary">No capabilities reported</p>
|
||||
<p style="opacity: 0.7;">No capabilities reported</p>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons - Full Width -->
|
||||
<div class="tw-flex tw-gap-2 tw-flex-wrap tw-mt-4">
|
||||
<button class="ui primary button" form="runner-form" data-url="{{.Link}}">
|
||||
{{svg "octicon-check" 14}} Update Instructions
|
||||
</button>
|
||||
{{if .RunnerCapabilities}}
|
||||
<button class="ui teal button" type="button" onclick="document.getElementById('suggested-labels-form').submit()">
|
||||
{{svg "octicon-light-bulb" 14}} Use All Suggested Labels
|
||||
</button>
|
||||
{{end}}
|
||||
<button class="ui secondary button" type="button" onclick="document.getElementById('bandwidth-form').submit()">
|
||||
{{svg "octicon-sync" 14}} Check Bandwidth
|
||||
</button>
|
||||
<button class="ui red button delete-button" data-url="{{.Link}}/delete" data-modal="#runner-delete-modal" type="button">
|
||||
{{svg "octicon-trash" 14}} Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Hidden Forms -->
|
||||
<form id="bandwidth-form" method="post" action="{{.Link}}/bandwidth-test" style="display:none">
|
||||
{{.CsrfTokenHtml}}
|
||||
</form>
|
||||
<form id="suggested-labels-form" method="post" action="{{.Link}}/use-suggested-labels" style="display:none">
|
||||
{{.CsrfTokenHtml}}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<h4 class="ui top attached header">
|
||||
|
||||
Reference in New Issue
Block a user