loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(cli): add `--keep-labels` flag to `forgejo actions register` (#4610)

This commit adds a new flag, `--keep-labels`, to the runner registration CLI command. If this flag is present and the runner being registered already exists, it will prevent the runners' labels from being reset.

In order to accomplish this, the signature of the `RegisterRunner` function from the `models/actions` package has been modified so that the labels argument can be nil. If it is, the part of the function that updates the record will not change the runner.

Various tests have been added for this function, for the following cases: new runner with labels, new runner without label, existing runner with labels, existing runner without labels.

The flag has been added to the CLI command, the action function has been updated to read the labels parameters through a separate function (`getLabels`), and test cases for this function have been added.

<!--
Before submitting a PR, please read the contributing guidelines:
https://codeberg.org/forgejo/forgejo/src/branch/forgejo/CONTRIBUTING.md
-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4610
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Emmanuel BENOÎT <tseeker@nocternity.net>
Co-committed-by: Emmanuel BENOÎT <tseeker@nocternity.net>

authored by

Emmanuel BENOÎT
Emmanuel BENOÎT
and committed by
Earl Warren
fdb1874a 8030ebf6

+241 -11
+21 -2
cmd/forgejo/actions.go
··· 86 86 Value: "", 87 87 Usage: "comma separated list of labels supported by the runner (e.g. docker,ubuntu-latest,self-hosted) (not required since v1.21)", 88 88 }, 89 + &cli.BoolFlag{ 90 + Name: "keep-labels", 91 + Value: false, 92 + Usage: "do not affect the labels when updating an existing runner", 93 + }, 89 94 &cli.StringFlag{ 90 95 Name: "name", 91 96 Value: "runner", ··· 133 138 return nil 134 139 } 135 140 141 + func getLabels(cliCtx *cli.Context) (*[]string, error) { 142 + if !cliCtx.Bool("keep-labels") { 143 + lblValue := strings.Split(cliCtx.String("labels"), ",") 144 + return &lblValue, nil 145 + } 146 + if cliCtx.String("labels") != "" { 147 + return nil, fmt.Errorf("--labels and --keep-labels should not be used together") 148 + } 149 + return nil, nil 150 + } 151 + 136 152 func RunRegister(ctx context.Context, cliCtx *cli.Context) error { 137 153 var cancel context.CancelFunc 138 154 if !ContextGetNoInit(ctx) { ··· 153 169 return err 154 170 } 155 171 scope := cliCtx.String("scope") 156 - labels := cliCtx.String("labels") 157 172 name := cliCtx.String("name") 158 173 version := cliCtx.String("version") 174 + labels, err := getLabels(cliCtx) 175 + if err != nil { 176 + return err 177 + } 159 178 160 179 // 161 180 // There are two kinds of tokens ··· 179 198 return err 180 199 } 181 200 182 - runner, err := actions_model.RegisterRunner(ctx, owner, repo, secret, strings.Split(labels, ","), name, version) 201 + runner, err := actions_model.RegisterRunner(ctx, owner, repo, secret, labels, name, version) 183 202 if err != nil { 184 203 return fmt.Errorf("error while registering runner: %v", err) 185 204 }
+88
cmd/forgejo/actions_test.go
··· 1 + // Copyright The Forgejo Authors. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package forgejo 5 + 6 + import ( 7 + "fmt" 8 + "testing" 9 + 10 + "code.gitea.io/gitea/services/context" 11 + 12 + "github.com/stretchr/testify/assert" 13 + "github.com/stretchr/testify/require" 14 + "github.com/urfave/cli/v2" 15 + ) 16 + 17 + func TestActions_getLabels(t *testing.T) { 18 + type testCase struct { 19 + args []string 20 + hasLabels bool 21 + hasError bool 22 + labels []string 23 + } 24 + type resultType struct { 25 + labels *[]string 26 + err error 27 + } 28 + 29 + cases := []testCase{ 30 + { 31 + args: []string{"x"}, 32 + hasLabels: true, 33 + hasError: false, 34 + labels: []string{""}, 35 + }, { 36 + args: []string{"x", "--labels", "a,b"}, 37 + hasLabels: true, 38 + hasError: false, 39 + labels: []string{"a", "b"}, 40 + }, { 41 + args: []string{"x", "--keep-labels"}, 42 + hasLabels: false, 43 + hasError: false, 44 + }, { 45 + args: []string{"x", "--keep-labels", "--labels", "a,b"}, 46 + hasLabels: false, 47 + hasError: true, 48 + }, { 49 + // this edge-case exists because that's what actually happens 50 + // when no '--labels ...' options are present 51 + args: []string{"x", "--keep-labels", "--labels", ""}, 52 + hasLabels: false, 53 + hasError: false, 54 + }, 55 + } 56 + 57 + flags := SubcmdActionsRegister(context.Context{}).Flags 58 + for _, c := range cases { 59 + t.Run(fmt.Sprintf("args: %v", c.args), func(t *testing.T) { 60 + // Create a copy of command to test 61 + var result *resultType 62 + app := cli.NewApp() 63 + app.Flags = flags 64 + app.Action = func(ctx *cli.Context) error { 65 + labels, err := getLabels(ctx) 66 + result = &resultType{labels, err} 67 + return nil 68 + } 69 + 70 + // Run it 71 + _ = app.Run(c.args) 72 + 73 + // Test the results 74 + require.NotNil(t, result) 75 + if c.hasLabels { 76 + assert.NotNil(t, result.labels) 77 + assert.Equal(t, c.labels, *result.labels) 78 + } else { 79 + assert.Nil(t, result.labels) 80 + } 81 + if c.hasError { 82 + assert.NotNil(t, result.err) 83 + } else { 84 + assert.Nil(t, result.err) 85 + } 86 + }) 87 + } 88 + }
+11 -6
models/actions/forgejo.go
··· 14 14 gouuid "github.com/google/uuid" 15 15 ) 16 16 17 - func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, labels []string, name, version string) (*ActionRunner, error) { 17 + func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, labels *[]string, name, version string) (*ActionRunner, error) { 18 18 uuid, err := gouuid.FromBytes([]byte(token[:16])) 19 19 if err != nil { 20 20 return nil, fmt.Errorf("gouuid.FromBytes %v", err) ··· 39 39 hash := auth_model.HashToken(token, salt) 40 40 41 41 runner = ActionRunner{ 42 - UUID: uuidString, 43 - TokenHash: hash, 44 - TokenSalt: salt, 42 + UUID: uuidString, 43 + TokenHash: hash, 44 + TokenSalt: salt, 45 + AgentLabels: []string{}, 45 46 } 46 47 47 48 if err := CreateRunner(ctx, &runner); err != nil { ··· 54 55 // 55 56 name, _ = util.SplitStringAtByteN(name, 255) 56 57 58 + cols := []string{"name", "owner_id", "repo_id", "version"} 57 59 runner.Name = name 58 60 runner.OwnerID = ownerID 59 61 runner.RepoID = repoID 60 62 runner.Version = version 61 - runner.AgentLabels = labels 63 + if labels != nil { 64 + runner.AgentLabels = *labels 65 + cols = append(cols, "agent_labels") 66 + } 62 67 63 - if err := UpdateRunner(ctx, &runner, "name", "owner_id", "repo_id", "version", "agent_labels"); err != nil { 68 + if err := UpdateRunner(ctx, &runner, cols...); err != nil { 64 69 return &runner, fmt.Errorf("can't update the runner %+v %w", runner, err) 65 70 } 66 71
+120 -2
models/actions/forgejo_test.go
··· 13 13 "github.com/stretchr/testify/assert" 14 14 ) 15 15 16 - func TestActions_RegisterRunner(t *testing.T) { 16 + func TestActions_RegisterRunner_Token(t *testing.T) { 17 17 assert.NoError(t, unittest.PrepareTestDatabase()) 18 18 ownerID := int64(0) 19 19 repoID := int64(0) ··· 21 21 labels := []string{} 22 22 name := "runner" 23 23 version := "v1.2.3" 24 - runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, labels, name, version) 24 + runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version) 25 25 assert.NoError(t, err) 26 26 assert.EqualValues(t, name, runner.Name) 27 27 28 28 assert.EqualValues(t, 1, subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))), "the token cannot be verified with the same method as routers/api/actions/runner/interceptor.go as of 8228751c55d6a4263f0fec2932ca16181c09c97d") 29 29 } 30 + 31 + func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) { 32 + assert.NoError(t, unittest.PrepareTestDatabase()) 33 + ownerID := int64(0) 34 + repoID := int64(0) 35 + token := "0123456789012345678901234567890123456789" 36 + name := "runner" 37 + version := "v1.2.3" 38 + labels := []string{"woop", "doop"} 39 + labelsCopy := labels // labels may be affected by the tested function so we copy them 40 + 41 + runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version) 42 + assert.NoError(t, err) 43 + 44 + // Check that the returned record has been updated, except for the labels 45 + assert.EqualValues(t, ownerID, runner.OwnerID) 46 + assert.EqualValues(t, repoID, runner.RepoID) 47 + assert.EqualValues(t, name, runner.Name) 48 + assert.EqualValues(t, version, runner.Version) 49 + assert.EqualValues(t, labelsCopy, runner.AgentLabels) 50 + 51 + // Check that whatever is in the DB has been updated, except for the labels 52 + after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID}) 53 + assert.EqualValues(t, ownerID, after.OwnerID) 54 + assert.EqualValues(t, repoID, after.RepoID) 55 + assert.EqualValues(t, name, after.Name) 56 + assert.EqualValues(t, version, after.Version) 57 + assert.EqualValues(t, labelsCopy, after.AgentLabels) 58 + } 59 + 60 + func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) { 61 + assert.NoError(t, unittest.PrepareTestDatabase()) 62 + ownerID := int64(0) 63 + repoID := int64(0) 64 + token := "0123456789012345678901234567890123456789" 65 + name := "runner" 66 + version := "v1.2.3" 67 + 68 + runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, nil, name, version) 69 + assert.NoError(t, err) 70 + 71 + // Check that the returned record has been updated, except for the labels 72 + assert.EqualValues(t, ownerID, runner.OwnerID) 73 + assert.EqualValues(t, repoID, runner.RepoID) 74 + assert.EqualValues(t, name, runner.Name) 75 + assert.EqualValues(t, version, runner.Version) 76 + assert.EqualValues(t, []string{}, runner.AgentLabels) 77 + 78 + // Check that whatever is in the DB has been updated, except for the labels 79 + after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID}) 80 + assert.EqualValues(t, ownerID, after.OwnerID) 81 + assert.EqualValues(t, repoID, after.RepoID) 82 + assert.EqualValues(t, name, after.Name) 83 + assert.EqualValues(t, version, after.Version) 84 + assert.EqualValues(t, []string{}, after.AgentLabels) 85 + } 86 + 87 + func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) { 88 + const recordID = 12345678 89 + token := "7e577e577e577e57feedfacefeedfacefeedface" 90 + assert.NoError(t, unittest.PrepareTestDatabase()) 91 + unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) 92 + 93 + newOwnerID := int64(1) 94 + newRepoID := int64(1) 95 + newName := "rennur" 96 + newVersion := "v4.5.6" 97 + newLabels := []string{"warp", "darp"} 98 + labelsCopy := newLabels // labels may be affected by the tested function so we copy them 99 + 100 + runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, &newLabels, newName, newVersion) 101 + assert.NoError(t, err) 102 + 103 + // Check that the returned record has been updated 104 + assert.EqualValues(t, newOwnerID, runner.OwnerID) 105 + assert.EqualValues(t, newRepoID, runner.RepoID) 106 + assert.EqualValues(t, newName, runner.Name) 107 + assert.EqualValues(t, newVersion, runner.Version) 108 + assert.EqualValues(t, labelsCopy, runner.AgentLabels) 109 + 110 + // Check that whatever is in the DB has been updated 111 + after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) 112 + assert.EqualValues(t, newOwnerID, after.OwnerID) 113 + assert.EqualValues(t, newRepoID, after.RepoID) 114 + assert.EqualValues(t, newName, after.Name) 115 + assert.EqualValues(t, newVersion, after.Version) 116 + assert.EqualValues(t, labelsCopy, after.AgentLabels) 117 + } 118 + 119 + func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) { 120 + const recordID = 12345678 121 + token := "7e577e577e577e57feedfacefeedfacefeedface" 122 + assert.NoError(t, unittest.PrepareTestDatabase()) 123 + before := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) 124 + 125 + newOwnerID := int64(1) 126 + newRepoID := int64(1) 127 + newName := "rennur" 128 + newVersion := "v4.5.6" 129 + 130 + runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, nil, newName, newVersion) 131 + assert.NoError(t, err) 132 + 133 + // Check that the returned record has been updated, except for the labels 134 + assert.EqualValues(t, newOwnerID, runner.OwnerID) 135 + assert.EqualValues(t, newRepoID, runner.RepoID) 136 + assert.EqualValues(t, newName, runner.Name) 137 + assert.EqualValues(t, newVersion, runner.Version) 138 + assert.EqualValues(t, before.AgentLabels, runner.AgentLabels) 139 + 140 + // Check that whatever is in the DB has been updated, except for the labels 141 + after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) 142 + assert.EqualValues(t, newOwnerID, after.OwnerID) 143 + assert.EqualValues(t, newRepoID, after.RepoID) 144 + assert.EqualValues(t, newName, after.Name) 145 + assert.EqualValues(t, newVersion, after.Version) 146 + assert.EqualValues(t, before.AgentLabels, after.AgentLabels) 147 + }
+1 -1
models/fixtures/action_runner.yml
··· 14 14 token_salt: "832f8529db6151a1c3c605dd7570b58f" 15 15 last_online: 0 16 16 last_active: 0 17 - agent_labels: '[""]' 17 + agent_labels: '["woop", "doop"]' 18 18 created: 1716104432 19 19 updated: 1716104432 20 20 deleted: ~