mirror of
https://github.com/go-gitea/gitea.git
synced 2024-11-22 13:20:33 +08:00
Support changing labels of Actions runner without re-registration (#24806)
close #24540 related: - Protocol: https://gitea.com/gitea/actions-proto-def/pulls/9 - Runner side: https://gitea.com/gitea/act_runner/pulls/201 changes: - Add column of `labels` to table `action_runner`, and combine the value of `agent_labels` and `custom_labels` column to `labels` column. - Store `labels` when registering `act_runner`. - Update `labels` when `act_runner` starting and calling `Declare`. - Users cannot modify the `custom labels` in edit page any more. other changes: - Store `version` when registering `act_runner`. - If runner is latest version, parse version from `Declare`. But older version runner still parse version from request header.
This commit is contained in:
parent
6bbccdd177
commit
8228751c55
2
go.mod
2
go.mod
|
@ -3,7 +3,7 @@ module code.gitea.io/gitea
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.gitea.io/actions-proto-go v0.2.1
|
code.gitea.io/actions-proto-go v0.3.0
|
||||||
code.gitea.io/gitea-vet v0.2.2
|
code.gitea.io/gitea-vet v0.2.2
|
||||||
code.gitea.io/sdk/gitea v0.15.1
|
code.gitea.io/sdk/gitea v0.15.1
|
||||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -40,8 +40,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||||
code.gitea.io/actions-proto-go v0.2.1 h1:ToMN/8thz2q10TuCq8dL2d8mI+/pWpJcHCvG+TELwa0=
|
code.gitea.io/actions-proto-go v0.3.0 h1:9Tvg8+TaaCXPKi6EnWl9vVgs2VZsj1Cs5afnsHa4AmM=
|
||||||
code.gitea.io/actions-proto-go v0.2.1/go.mod h1:00ys5QDo1iHN1tHNvvddAcy2W/g+425hQya1cCSvq9A=
|
code.gitea.io/actions-proto-go v0.3.0/go.mod h1:00ys5QDo1iHN1tHNvvddAcy2W/g+425hQya1cCSvq9A=
|
||||||
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
||||||
code.gitea.io/gitea-vet v0.2.2 h1:TEOV/Glf38iGmKzKP0EB++Z5OSL4zGg3RrAvlwaMuvk=
|
code.gitea.io/gitea-vet v0.2.2 h1:TEOV/Glf38iGmKzKP0EB++Z5OSL4zGg3RrAvlwaMuvk=
|
||||||
code.gitea.io/gitea-vet v0.2.2/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
code.gitea.io/gitea-vet v0.2.2/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
||||||
|
|
|
@ -43,10 +43,8 @@ type ActionRunner struct {
|
||||||
LastOnline timeutil.TimeStamp `xorm:"index"`
|
LastOnline timeutil.TimeStamp `xorm:"index"`
|
||||||
LastActive timeutil.TimeStamp `xorm:"index"`
|
LastActive timeutil.TimeStamp `xorm:"index"`
|
||||||
|
|
||||||
// Store OS and Artch.
|
// Store labels defined in state file (default: .runner file) of `act_runner`
|
||||||
AgentLabels []string
|
AgentLabels []string `xorm:"TEXT"`
|
||||||
// Store custom labes use defined.
|
|
||||||
CustomLabels []string
|
|
||||||
|
|
||||||
Created timeutil.TimeStamp `xorm:"created"`
|
Created timeutil.TimeStamp `xorm:"created"`
|
||||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||||
|
@ -104,11 +102,6 @@ func (r *ActionRunner) IsOnline() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllLabels returns agent and custom labels
|
|
||||||
func (r *ActionRunner) AllLabels() []string {
|
|
||||||
return append(r.AgentLabels, r.CustomLabels...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Editable checks if the runner is editable by the user
|
// Editable checks if the runner is editable by the user
|
||||||
func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
|
func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
|
||||||
if ownerID == 0 && repoID == 0 {
|
if ownerID == 0 && repoID == 0 {
|
||||||
|
|
|
@ -241,11 +241,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
||||||
|
|
||||||
// TODO: a more efficient way to filter labels
|
// TODO: a more efficient way to filter labels
|
||||||
var job *ActionRunJob
|
var job *ActionRunJob
|
||||||
labels := runner.AgentLabels
|
log.Trace("runner labels: %v", runner.AgentLabels)
|
||||||
labels = append(labels, runner.CustomLabels...)
|
|
||||||
log.Trace("runner labels: %v", labels)
|
|
||||||
for _, v := range jobs {
|
for _, v := range jobs {
|
||||||
if isSubset(labels, v.RunsOn) {
|
if isSubset(runner.AgentLabels, v.RunsOn) {
|
||||||
job = v
|
job = v
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/migrations/v1_18"
|
"code.gitea.io/gitea/models/migrations/v1_18"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_19"
|
"code.gitea.io/gitea/models/migrations/v1_19"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_20"
|
"code.gitea.io/gitea/models/migrations/v1_20"
|
||||||
|
"code.gitea.io/gitea/models/migrations/v1_21"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_6"
|
"code.gitea.io/gitea/models/migrations/v1_6"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_7"
|
"code.gitea.io/gitea/models/migrations/v1_7"
|
||||||
"code.gitea.io/gitea/models/migrations/v1_8"
|
"code.gitea.io/gitea/models/migrations/v1_8"
|
||||||
|
@ -497,6 +498,11 @@ var migrations = []Migration{
|
||||||
NewMigration("Add PinOrder Column", v1_20.AddPinOrderToIssue),
|
NewMigration("Add PinOrder Column", v1_20.AddPinOrderToIssue),
|
||||||
// v259 -> 260
|
// v259 -> 260
|
||||||
NewMigration("Convert scoped access tokens", v1_20.ConvertScopedAccessTokens),
|
NewMigration("Convert scoped access tokens", v1_20.ConvertScopedAccessTokens),
|
||||||
|
|
||||||
|
// Gitea 1.21.0 ends at 260
|
||||||
|
|
||||||
|
// v260 -> v261
|
||||||
|
NewMigration("Add label column to action_run table, and combine labels", v1_21.DropCustomLabelsColumnToActRunner),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
|
14
models/migrations/v1_21/main_test.go
Normal file
14
models/migrations/v1_21/main_test.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_21 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
base.MainTest(m)
|
||||||
|
}
|
26
models/migrations/v1_21/v260.go
Normal file
26
models/migrations/v1_21/v260.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_21 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DropCustomLabelsColumnToActRunner(x *xorm.Engine) error {
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop "custom_labels" cols
|
||||||
|
if err := base.DropTableColumns(sess, "action_runner", "custom_labels"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
|
@ -3426,11 +3426,9 @@ runners.owner_type = Type
|
||||||
runners.description = Description
|
runners.description = Description
|
||||||
runners.labels = Labels
|
runners.labels = Labels
|
||||||
runners.last_online = Last Online Time
|
runners.last_online = Last Online Time
|
||||||
runners.agent_labels = Agent Labels
|
|
||||||
runners.custom_labels = Custom Labels
|
|
||||||
runners.custom_labels_helper = Custom labels are labels that are added manually by an administrator. A comma separates labels, whitespace at the start and end of each label is ignored.
|
|
||||||
runners.runner_title = Runner
|
runners.runner_title = Runner
|
||||||
runners.task_list = Recent tasks on this runner
|
runners.task_list = Recent tasks on this runner
|
||||||
|
runners.task_list.no_tasks = There is no task yet.
|
||||||
runners.task_list.run = Run
|
runners.task_list.run = Run
|
||||||
runners.task_list.status = Status
|
runners.task_list.status = Status
|
||||||
runners.task_list.repository = Repository
|
runners.task_list.repository = Repository
|
||||||
|
|
|
@ -21,11 +21,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
uuidHeaderKey = "x-runner-uuid"
|
uuidHeaderKey = "x-runner-uuid"
|
||||||
tokenHeaderKey = "x-runner-token"
|
tokenHeaderKey = "x-runner-token"
|
||||||
|
// Deprecated: will be removed after Gitea 1.20 released.
|
||||||
versionHeaderKey = "x-runner-version"
|
versionHeaderKey = "x-runner-version"
|
||||||
|
|
||||||
versionUnknown = "Unknown"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unaryFunc connect.UnaryFunc) connect.UnaryFunc {
|
var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unaryFunc connect.UnaryFunc) connect.UnaryFunc {
|
||||||
|
@ -36,11 +35,9 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
|
||||||
}
|
}
|
||||||
uuid := request.Header().Get(uuidHeaderKey)
|
uuid := request.Header().Get(uuidHeaderKey)
|
||||||
token := request.Header().Get(tokenHeaderKey)
|
token := request.Header().Get(tokenHeaderKey)
|
||||||
|
// TODO: version will be removed from request header after Gitea 1.20 released.
|
||||||
|
// And Gitea will not try to read version from reuqest header
|
||||||
version := request.Header().Get(versionHeaderKey)
|
version := request.Header().Get(versionHeaderKey)
|
||||||
if util.IsEmptyString(version) {
|
|
||||||
version = versionUnknown
|
|
||||||
}
|
|
||||||
version, _ = util.SplitStringAtByteN(version, 64)
|
|
||||||
|
|
||||||
runner, err := actions_model.GetRunnerByUUID(ctx, uuid)
|
runner, err := actions_model.GetRunnerByUUID(ctx, uuid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,7 +51,11 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
|
||||||
}
|
}
|
||||||
|
|
||||||
cols := []string{"last_online"}
|
cols := []string{"last_online"}
|
||||||
if runner.Version != version {
|
|
||||||
|
// TODO: version will be removed from request header after Gitea 1.20 released.
|
||||||
|
// And Gitea will not try to read version from reuqest header
|
||||||
|
version, _ = util.SplitStringAtByteN(version, 64)
|
||||||
|
if !util.IsEmptyString(version) && runner.Version != version {
|
||||||
runner.Version = version
|
runner.Version = version
|
||||||
cols = append(cols, "version")
|
cols = append(cols, "version")
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,15 +54,23 @@ func (s *Service) Register(
|
||||||
return nil, errors.New("runner token has already been activated")
|
return nil, errors.New("runner token has already been activated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
labels := req.Msg.Labels
|
||||||
|
// TODO: agent_labels should be removed from pb after Gitea 1.20 released.
|
||||||
|
// Old version runner's agent_labels slice is not empty and labels slice is empty.
|
||||||
|
// And due to compatibility with older versions, it is temporarily marked as Deprecated in pb, so use `//nolint` here.
|
||||||
|
if len(req.Msg.AgentLabels) > 0 && len(req.Msg.Labels) == 0 { //nolint:staticcheck
|
||||||
|
labels = req.Msg.AgentLabels //nolint:staticcheck
|
||||||
|
}
|
||||||
|
|
||||||
// create new runner
|
// create new runner
|
||||||
name, _ := util.SplitStringAtByteN(req.Msg.Name, 255)
|
name, _ := util.SplitStringAtByteN(req.Msg.Name, 255)
|
||||||
runner := &actions_model.ActionRunner{
|
runner := &actions_model.ActionRunner{
|
||||||
UUID: gouuid.New().String(),
|
UUID: gouuid.New().String(),
|
||||||
Name: name,
|
Name: name,
|
||||||
OwnerID: runnerToken.OwnerID,
|
OwnerID: runnerToken.OwnerID,
|
||||||
RepoID: runnerToken.RepoID,
|
RepoID: runnerToken.RepoID,
|
||||||
AgentLabels: req.Msg.AgentLabels,
|
Version: req.Msg.Version,
|
||||||
CustomLabels: req.Msg.CustomLabels,
|
AgentLabels: labels,
|
||||||
}
|
}
|
||||||
if err := runner.GenerateToken(); err != nil {
|
if err := runner.GenerateToken(); err != nil {
|
||||||
return nil, errors.New("can't generate token")
|
return nil, errors.New("can't generate token")
|
||||||
|
@ -81,18 +89,41 @@ func (s *Service) Register(
|
||||||
|
|
||||||
res := connect.NewResponse(&runnerv1.RegisterResponse{
|
res := connect.NewResponse(&runnerv1.RegisterResponse{
|
||||||
Runner: &runnerv1.Runner{
|
Runner: &runnerv1.Runner{
|
||||||
Id: runner.ID,
|
Id: runner.ID,
|
||||||
Uuid: runner.UUID,
|
Uuid: runner.UUID,
|
||||||
Token: runner.Token,
|
Token: runner.Token,
|
||||||
Name: runner.Name,
|
Name: runner.Name,
|
||||||
AgentLabels: runner.AgentLabels,
|
Version: runner.Version,
|
||||||
CustomLabels: runner.CustomLabels,
|
Labels: runner.AgentLabels,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) Declare(
|
||||||
|
ctx context.Context,
|
||||||
|
req *connect.Request[runnerv1.DeclareRequest],
|
||||||
|
) (*connect.Response[runnerv1.DeclareResponse], error) {
|
||||||
|
runner := GetRunner(ctx)
|
||||||
|
runner.AgentLabels = req.Msg.Labels
|
||||||
|
runner.Version = req.Msg.Version
|
||||||
|
if err := actions_model.UpdateRunner(ctx, runner, "agent_labels", "version"); err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "update runner: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return connect.NewResponse(&runnerv1.DeclareResponse{
|
||||||
|
Runner: &runnerv1.Runner{
|
||||||
|
Id: runner.ID,
|
||||||
|
Uuid: runner.UUID,
|
||||||
|
Token: runner.Token,
|
||||||
|
Name: runner.Name,
|
||||||
|
Version: runner.Version,
|
||||||
|
Labels: runner.AgentLabels,
|
||||||
|
},
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
// FetchTask assigns a task to the runner
|
// FetchTask assigns a task to the runner
|
||||||
func (s *Service) FetchTask(
|
func (s *Service) FetchTask(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
@ -84,7 +84,6 @@ func List(ctx *context.Context) {
|
||||||
allRunnerLabels := make(container.Set[string])
|
allRunnerLabels := make(container.Set[string])
|
||||||
for _, r := range runners {
|
for _, r := range runners {
|
||||||
allRunnerLabels.AddMultiple(r.AgentLabels...)
|
allRunnerLabels.AddMultiple(r.AgentLabels...)
|
||||||
allRunnerLabels.AddMultiple(r.CustomLabels...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
workflows = make([]Workflow, 0, len(entries))
|
workflows = make([]Workflow, 0, len(entries))
|
||||||
|
|
|
@ -6,7 +6,6 @@ package actions
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
@ -126,9 +125,8 @@ func RunnerDetailsEditPost(ctx *context.Context, runnerID, ownerID, repoID int64
|
||||||
|
|
||||||
form := web.GetForm(ctx).(*forms.EditRunnerForm)
|
form := web.GetForm(ctx).(*forms.EditRunnerForm)
|
||||||
runner.Description = form.Description
|
runner.Description = form.Description
|
||||||
runner.CustomLabels = splitLabels(form.CustomLabels)
|
|
||||||
|
|
||||||
err = actions_model.UpdateRunner(ctx, runner, "description", "custom_labels")
|
err = actions_model.UpdateRunner(ctx, runner, "description")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("RunnerDetailsEditPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL)
|
log.Warn("RunnerDetailsEditPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL)
|
||||||
ctx.Flash.Warning(ctx.Tr("actions.runners.update_runner_failed"))
|
ctx.Flash.Warning(ctx.Tr("actions.runners.update_runner_failed"))
|
||||||
|
@ -176,11 +174,3 @@ func RunnerDeletePost(ctx *context.Context, runnerID int64,
|
||||||
"redirect": successRedirectTo,
|
"redirect": successRedirectTo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitLabels(s string) []string {
|
|
||||||
labels := strings.Split(s, ",")
|
|
||||||
for i, v := range labels {
|
|
||||||
labels[i] = strings.TrimSpace(v)
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,8 +14,7 @@ import (
|
||||||
|
|
||||||
// EditRunnerForm form for admin to create runner
|
// EditRunnerForm form for admin to create runner
|
||||||
type EditRunnerForm struct {
|
type EditRunnerForm struct {
|
||||||
Description string
|
Description string
|
||||||
CustomLabels string // comma-separated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates form fields
|
// Validate validates form fields
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="field gt-dib gt-mr-4">
|
<div class="field gt-dib gt-mr-4">
|
||||||
<label>{{.locale.Tr "actions.runners.last_online"}}</label>
|
<label>{{.locale.Tr "actions.runners.last_online"}}</label>
|
||||||
<span>{{if .LastOnline}}{{TimeSinceUnix .LastOnline $.locale}}{{else}}{{$.locale.Tr "never"}}{{end}}</span>
|
<span>{{if .Runner.LastOnline}}{{TimeSinceUnix .Runner.LastOnline $.locale}}{{else}}{{$.locale.Tr "never"}}{{end}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="field gt-dib gt-mr-4">
|
<div class="field gt-dib gt-mr-4">
|
||||||
<label>{{.locale.Tr "actions.runners.agent_labels"}}</label>
|
<label>{{.locale.Tr "actions.runners.labels"}}</label>
|
||||||
<span>
|
<span>
|
||||||
{{range .Runner.AgentLabels}}
|
{{range .Runner.AgentLabels}}
|
||||||
<span>{{.}}</span>
|
<span>{{.}}</span>
|
||||||
|
@ -35,11 +35,6 @@
|
||||||
<label for="description">{{.locale.Tr "actions.runners.description"}}</label>
|
<label for="description">{{.locale.Tr "actions.runners.description"}}</label>
|
||||||
<input id="description" name="description" value="{{.Runner.Description}}">
|
<input id="description" name="description" value="{{.Runner.Description}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="field" data-tooltip-content="Labels are comma-separated. Whitespace at the beginning, end, and around the commas are ignored.">
|
|
||||||
<label for="custom_labels">{{.locale.Tr "actions.runners.custom_labels"}}</label>
|
|
||||||
<input id="custom_labels" name="custom_labels" value="{{StringUtils.Join .Runner.CustomLabels `,`}}">
|
|
||||||
<p class="help">{{.locale.Tr "actions.runners.custom_labels_helper"}}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
|
@ -81,7 +76,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if not .Tasks}}
|
{{if not .Tasks}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5">{{.locale.Tr "runners.task_list.no_tasks"}}</td>
|
<td colspan="5">{{.locale.Tr "actions.runners.task_list.no_tasks"}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
<td>{{if .Version}}{{.Version}}{{else}}{{$.locale.Tr "unknown"}}{{end}}</td>
|
<td>{{if .Version}}{{.Version}}{{else}}{{$.locale.Tr "unknown"}}{{end}}</td>
|
||||||
<td><span data-tooltip-content="{{.BelongsToOwnerName}}">{{.BelongsToOwnerType.LocaleString $.locale}}<span></td>
|
<td><span data-tooltip-content="{{.BelongsToOwnerName}}">{{.BelongsToOwnerType.LocaleString $.locale}}<span></td>
|
||||||
<td class="runner-tags">
|
<td class="runner-tags">
|
||||||
{{range .AllLabels}}<span class="ui label">{{.}}</span>{{end}}
|
{{range .AgentLabels}}<span class="ui label">{{.}}</span>{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td>{{if .LastOnline}}{{TimeSinceUnix .LastOnline $.locale}}{{else}}{{$.locale.Tr "never"}}{{end}}</td>
|
<td>{{if .LastOnline}}{{TimeSinceUnix .LastOnline $.locale}}{{else}}{{$.locale.Tr "never"}}{{end}}</td>
|
||||||
<td class="runner-ops">
|
<td class="runner-ops">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user