Skip to content

Commit 8f051d5

Browse files
LaoQiwxiaoguang
andauthored
Added Description Field for Secrets and Variables (#33526)
Fixes #33484 --------- Co-authored-by: wxiaoguang <[email protected]>
1 parent 9d7c02f commit 8f051d5

File tree

20 files changed

+247
-63
lines changed

20 files changed

+247
-63
lines changed

models/actions/variable.go

+26-5
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ package actions
66
import (
77
"context"
88
"strings"
9+
"unicode/utf8"
910

1011
"code.gitea.io/gitea/models/db"
1112
"code.gitea.io/gitea/modules/log"
1213
"code.gitea.io/gitea/modules/timeutil"
14+
"code.gitea.io/gitea/modules/util"
1315

1416
"xorm.io/builder"
1517
)
@@ -32,26 +34,39 @@ type ActionVariable struct {
3234
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name)"`
3335
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
3436
Data string `xorm:"LONGTEXT NOT NULL"`
37+
Description string `xorm:"TEXT"`
3538
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
3639
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
3740
}
3841

42+
const (
43+
VariableDataMaxLength = 65536
44+
VariableDescriptionMaxLength = 4096
45+
)
46+
3947
func init() {
4048
db.RegisterModel(new(ActionVariable))
4149
}
4250

43-
func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*ActionVariable, error) {
51+
func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data, description string) (*ActionVariable, error) {
4452
if ownerID != 0 && repoID != 0 {
4553
// It's trying to create a variable that belongs to a repository, but OwnerID has been set accidentally.
4654
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
4755
ownerID = 0
4856
}
4957

58+
if utf8.RuneCountInString(data) > VariableDataMaxLength {
59+
return nil, util.NewInvalidArgumentErrorf("data too long")
60+
}
61+
62+
description = util.TruncateRunes(description, VariableDescriptionMaxLength)
63+
5064
variable := &ActionVariable{
51-
OwnerID: ownerID,
52-
RepoID: repoID,
53-
Name: strings.ToUpper(name),
54-
Data: data,
65+
OwnerID: ownerID,
66+
RepoID: repoID,
67+
Name: strings.ToUpper(name),
68+
Data: data,
69+
Description: description,
5570
}
5671
return variable, db.Insert(ctx, variable)
5772
}
@@ -96,6 +111,12 @@ func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariab
96111
}
97112

98113
func UpdateVariableCols(ctx context.Context, variable *ActionVariable, cols ...string) (bool, error) {
114+
if utf8.RuneCountInString(variable.Data) > VariableDataMaxLength {
115+
return false, util.NewInvalidArgumentErrorf("data too long")
116+
}
117+
118+
variable.Description = util.TruncateRunes(variable.Description, VariableDescriptionMaxLength)
119+
99120
variable.Name = strings.ToUpper(variable.Name)
100121
count, err := db.GetEngine(ctx).
101122
ID(variable.ID).

models/migrations/migrations.go

+1
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@ func prepareMigrationTasks() []*migration {
376376
newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin),
377377
newMigration(314, "Update OwnerID as zero for repository level action tables", v1_24.UpdateOwnerIDOfRepoLevelActionsTables),
378378
newMigration(315, "Add Ephemeral to ActionRunner", v1_24.AddEphemeralToActionRunner),
379+
newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables),
379380
}
380381
return preparedMigrations
381382
}

models/migrations/v1_24/v316.go

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_24 //nolint
5+
6+
import (
7+
"xorm.io/xorm"
8+
)
9+
10+
func AddDescriptionForSecretsAndVariables(x *xorm.Engine) error {
11+
type Secret struct {
12+
Description string `xorm:"TEXT"`
13+
}
14+
15+
type ActionVariable struct {
16+
Description string `xorm:"TEXT"`
17+
}
18+
19+
return x.Sync(new(Secret), new(ActionVariable))
20+
}

models/secret/secret.go

+29-8
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,15 @@ type Secret struct {
4040
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL DEFAULT 0"`
4141
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
4242
Data string `xorm:"LONGTEXT"` // encrypted data
43+
Description string `xorm:"TEXT"`
4344
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
4445
}
4546

47+
const (
48+
SecretDataMaxLength = 65536
49+
SecretDescriptionMaxLength = 4096
50+
)
51+
4652
// ErrSecretNotFound represents a "secret not found" error.
4753
type ErrSecretNotFound struct {
4854
Name string
@@ -57,7 +63,7 @@ func (err ErrSecretNotFound) Unwrap() error {
5763
}
5864

5965
// InsertEncryptedSecret Creates, encrypts, and validates a new secret with yet unencrypted data and insert into database
60-
func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data string) (*Secret, error) {
66+
func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, data, description string) (*Secret, error) {
6167
if ownerID != 0 && repoID != 0 {
6268
// It's trying to create a secret that belongs to a repository, but OwnerID has been set accidentally.
6369
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
@@ -67,15 +73,23 @@ func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, dat
6773
return nil, fmt.Errorf("%w: ownerID and repoID cannot be both zero, global secrets are not supported", util.ErrInvalidArgument)
6874
}
6975

76+
if len(data) > SecretDataMaxLength {
77+
return nil, util.NewInvalidArgumentErrorf("data too long")
78+
}
79+
80+
description = util.TruncateRunes(description, SecretDescriptionMaxLength)
81+
7082
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
7183
if err != nil {
7284
return nil, err
7385
}
86+
7487
secret := &Secret{
75-
OwnerID: ownerID,
76-
RepoID: repoID,
77-
Name: strings.ToUpper(name),
78-
Data: encrypted,
88+
OwnerID: ownerID,
89+
RepoID: repoID,
90+
Name: strings.ToUpper(name),
91+
Data: encrypted,
92+
Description: description,
7993
}
8094
return secret, db.Insert(ctx, secret)
8195
}
@@ -114,16 +128,23 @@ func (opts FindSecretsOptions) ToConds() builder.Cond {
114128
}
115129

116130
// UpdateSecret changes org or user reop secret.
117-
func UpdateSecret(ctx context.Context, secretID int64, data string) error {
131+
func UpdateSecret(ctx context.Context, secretID int64, data, description string) error {
132+
if len(data) > SecretDataMaxLength {
133+
return util.NewInvalidArgumentErrorf("data too long")
134+
}
135+
136+
description = util.TruncateRunes(description, SecretDescriptionMaxLength)
137+
118138
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)
119139
if err != nil {
120140
return err
121141
}
122142

123143
s := &Secret{
124-
Data: encrypted,
144+
Data: encrypted,
145+
Description: description,
125146
}
126-
affected, err := db.GetEngine(ctx).ID(secretID).Cols("data").Update(s)
147+
affected, err := db.GetEngine(ctx).ID(secretID).Cols("data", "description").Update(s)
127148
if affected != 1 {
128149
return ErrSecretNotFound{}
129150
}

modules/structs/secret.go

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import "time"
1010
type Secret struct {
1111
// the secret's name
1212
Name string `json:"name"`
13+
// the secret's description
14+
Description string `json:"description"`
1315
// swagger:strfmt date-time
1416
Created time.Time `json:"created_at"`
1517
}
@@ -21,4 +23,9 @@ type CreateOrUpdateSecretOption struct {
2123
//
2224
// required: true
2325
Data string `json:"data" binding:"Required"`
26+
27+
// Description of the secret to update
28+
//
29+
// required: false
30+
Description string `json:"description"`
2431
}

modules/structs/variable.go

+12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ type CreateVariableOption struct {
1010
//
1111
// required: true
1212
Value string `json:"value" binding:"Required"`
13+
14+
// Description of the variable to create
15+
//
16+
// required: false
17+
Description string `json:"description"`
1318
}
1419

1520
// UpdateVariableOption the option when updating variable
@@ -21,6 +26,11 @@ type UpdateVariableOption struct {
2126
//
2227
// required: true
2328
Value string `json:"value" binding:"Required"`
29+
30+
// Description of the variable to update
31+
//
32+
// required: false
33+
Description string `json:"description"`
2434
}
2535

2636
// ActionVariable return value of the query API
@@ -34,4 +44,6 @@ type ActionVariable struct {
3444
Name string `json:"name"`
3545
// the value of the variable
3646
Data string `json:"data"`
47+
// the description of the variable
48+
Description string `json:"description"`
3749
}

options/locale/locale_en-US.ini

+2
Original file line numberDiff line numberDiff line change
@@ -3712,8 +3712,10 @@ secrets = Secrets
37123712
description = Secrets will be passed to certain actions and cannot be read otherwise.
37133713
none = There are no secrets yet.
37143714
creation = Add Secret
3715+
creation.description = Description
37153716
creation.name_placeholder = case-insensitive, alphanumeric characters or underscores only, cannot start with GITEA_ or GITHUB_
37163717
creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted.
3718+
creation.description_placeholder = Enter short description (optional).
37173719
creation.success = The secret "%s" has been added.
37183720
creation.failed = Failed to add secret.
37193721
deletion = Remove secret

routers/api/v1/org/action.go

+16-12
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) {
6161
apiSecrets := make([]*api.Secret, len(secrets))
6262
for k, v := range secrets {
6363
apiSecrets[k] = &api.Secret{
64-
Name: v.Name,
65-
Created: v.CreatedUnix.AsTime(),
64+
Name: v.Name,
65+
Description: v.Description,
66+
Created: v.CreatedUnix.AsTime(),
6667
}
6768
}
6869

@@ -106,7 +107,7 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
106107

107108
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
108109

109-
_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data)
110+
_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data, opt.Description)
110111
if err != nil {
111112
if errors.Is(err, util.ErrInvalidArgument) {
112113
ctx.APIError(http.StatusBadRequest, err)
@@ -230,10 +231,11 @@ func (Action) ListVariables(ctx *context.APIContext) {
230231
variables := make([]*api.ActionVariable, len(vars))
231232
for i, v := range vars {
232233
variables[i] = &api.ActionVariable{
233-
OwnerID: v.OwnerID,
234-
RepoID: v.RepoID,
235-
Name: v.Name,
236-
Data: v.Data,
234+
OwnerID: v.OwnerID,
235+
RepoID: v.RepoID,
236+
Name: v.Name,
237+
Data: v.Data,
238+
Description: v.Description,
237239
}
238240
}
239241

@@ -281,10 +283,11 @@ func (Action) GetVariable(ctx *context.APIContext) {
281283
}
282284

283285
variable := &api.ActionVariable{
284-
OwnerID: v.OwnerID,
285-
RepoID: v.RepoID,
286-
Name: v.Name,
287-
Data: v.Data,
286+
OwnerID: v.OwnerID,
287+
RepoID: v.RepoID,
288+
Name: v.Name,
289+
Data: v.Data,
290+
Description: v.Description,
288291
}
289292

290293
ctx.JSON(http.StatusOK, variable)
@@ -386,7 +389,7 @@ func (Action) CreateVariable(ctx *context.APIContext) {
386389
return
387390
}
388391

389-
if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil {
392+
if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value, opt.Description); err != nil {
390393
if errors.Is(err, util.ErrInvalidArgument) {
391394
ctx.APIError(http.StatusBadRequest, err)
392395
} else {
@@ -453,6 +456,7 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
453456

454457
v.Name = opt.Name
455458
v.Data = opt.Value
459+
v.Description = opt.Description
456460

457461
if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
458462
if errors.Is(err, util.ErrInvalidArgument) {

routers/api/v1/repo/action.go

+16-11
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) {
8484
apiSecrets := make([]*api.Secret, len(secrets))
8585
for k, v := range secrets {
8686
apiSecrets[k] = &api.Secret{
87-
Name: v.Name,
88-
Created: v.CreatedUnix.AsTime(),
87+
Name: v.Name,
88+
Description: v.Description,
89+
Created: v.CreatedUnix.AsTime(),
8990
}
9091
}
9192

@@ -136,7 +137,7 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
136137

137138
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
138139

139-
_, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.PathParam("secretname"), opt.Data)
140+
_, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.PathParam("secretname"), opt.Data, opt.Description)
140141
if err != nil {
141142
if errors.Is(err, util.ErrInvalidArgument) {
142143
ctx.APIError(http.StatusBadRequest, err)
@@ -249,10 +250,11 @@ func (Action) GetVariable(ctx *context.APIContext) {
249250
}
250251

251252
variable := &api.ActionVariable{
252-
OwnerID: v.OwnerID,
253-
RepoID: v.RepoID,
254-
Name: v.Name,
255-
Data: v.Data,
253+
OwnerID: v.OwnerID,
254+
RepoID: v.RepoID,
255+
Name: v.Name,
256+
Data: v.Data,
257+
Description: v.Description,
256258
}
257259

258260
ctx.JSON(http.StatusOK, variable)
@@ -362,7 +364,7 @@ func (Action) CreateVariable(ctx *context.APIContext) {
362364
return
363365
}
364366

365-
if _, err := actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value); err != nil {
367+
if _, err := actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value, opt.Description); err != nil {
366368
if errors.Is(err, util.ErrInvalidArgument) {
367369
ctx.APIError(http.StatusBadRequest, err)
368370
} else {
@@ -432,6 +434,7 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
432434

433435
v.Name = opt.Name
434436
v.Data = opt.Value
437+
v.Description = opt.Description
435438

436439
if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
437440
if errors.Is(err, util.ErrInvalidArgument) {
@@ -491,9 +494,11 @@ func (Action) ListVariables(ctx *context.APIContext) {
491494
variables := make([]*api.ActionVariable, len(vars))
492495
for i, v := range vars {
493496
variables[i] = &api.ActionVariable{
494-
OwnerID: v.OwnerID,
495-
RepoID: v.RepoID,
496-
Name: v.Name,
497+
OwnerID: v.OwnerID,
498+
RepoID: v.RepoID,
499+
Name: v.Name,
500+
Data: v.Data,
501+
Description: v.Description,
497502
}
498503
}
499504

0 commit comments

Comments
 (0)