code.gitea.io/gitea@v1.21.7/routers/web/shared/actions/variables.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package actions
     5  
     6  import (
     7  	"errors"
     8  	"regexp"
     9  	"strings"
    10  
    11  	actions_model "code.gitea.io/gitea/models/actions"
    12  	"code.gitea.io/gitea/models/db"
    13  	"code.gitea.io/gitea/modules/context"
    14  	"code.gitea.io/gitea/modules/log"
    15  	"code.gitea.io/gitea/modules/web"
    16  	"code.gitea.io/gitea/services/forms"
    17  	secret_service "code.gitea.io/gitea/services/secrets"
    18  )
    19  
    20  func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) {
    21  	variables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{
    22  		OwnerID: ownerID,
    23  		RepoID:  repoID,
    24  	})
    25  	if err != nil {
    26  		ctx.ServerError("FindVariables", err)
    27  		return
    28  	}
    29  	ctx.Data["Variables"] = variables
    30  }
    31  
    32  // some regular expression of `variables` and `secrets`
    33  // reference to:
    34  // https://docs.github.com/en/actions/learn-github-actions/variables#naming-conventions-for-configuration-variables
    35  // https://docs.github.com/en/actions/security-guides/encrypted-secrets#naming-your-secrets
    36  var (
    37  	forbiddenEnvNameCIRx = regexp.MustCompile("(?i)^CI")
    38  )
    39  
    40  func envNameCIRegexMatch(name string) error {
    41  	if forbiddenEnvNameCIRx.MatchString(name) {
    42  		log.Error("Env Name cannot be ci")
    43  		return errors.New("env name cannot be ci")
    44  	}
    45  	return nil
    46  }
    47  
    48  func CreateVariable(ctx *context.Context, ownerID, repoID int64, redirectURL string) {
    49  	form := web.GetForm(ctx).(*forms.EditVariableForm)
    50  
    51  	if err := secret_service.ValidateName(form.Name); err != nil {
    52  		ctx.JSONError(err.Error())
    53  		return
    54  	}
    55  
    56  	if err := envNameCIRegexMatch(form.Name); err != nil {
    57  		ctx.JSONError(err.Error())
    58  		return
    59  	}
    60  
    61  	v, err := actions_model.InsertVariable(ctx, ownerID, repoID, form.Name, ReserveLineBreakForTextarea(form.Data))
    62  	if err != nil {
    63  		log.Error("InsertVariable error: %v", err)
    64  		ctx.JSONError(ctx.Tr("actions.variables.creation.failed"))
    65  		return
    66  	}
    67  	ctx.Flash.Success(ctx.Tr("actions.variables.creation.success", v.Name))
    68  	ctx.JSONRedirect(redirectURL)
    69  }
    70  
    71  func UpdateVariable(ctx *context.Context, redirectURL string) {
    72  	id := ctx.ParamsInt64(":variable_id")
    73  	form := web.GetForm(ctx).(*forms.EditVariableForm)
    74  
    75  	if err := secret_service.ValidateName(form.Name); err != nil {
    76  		ctx.JSONError(err.Error())
    77  		return
    78  	}
    79  
    80  	if err := envNameCIRegexMatch(form.Name); err != nil {
    81  		ctx.JSONError(err.Error())
    82  		return
    83  	}
    84  
    85  	ok, err := actions_model.UpdateVariable(ctx, &actions_model.ActionVariable{
    86  		ID:   id,
    87  		Name: strings.ToUpper(form.Name),
    88  		Data: ReserveLineBreakForTextarea(form.Data),
    89  	})
    90  	if err != nil || !ok {
    91  		log.Error("UpdateVariable error: %v", err)
    92  		ctx.JSONError(ctx.Tr("actions.variables.update.failed"))
    93  		return
    94  	}
    95  	ctx.Flash.Success(ctx.Tr("actions.variables.update.success"))
    96  	ctx.JSONRedirect(redirectURL)
    97  }
    98  
    99  func DeleteVariable(ctx *context.Context, redirectURL string) {
   100  	id := ctx.ParamsInt64(":variable_id")
   101  
   102  	if _, err := db.DeleteByBean(ctx, &actions_model.ActionVariable{ID: id}); err != nil {
   103  		log.Error("Delete variable [%d] failed: %v", id, err)
   104  		ctx.JSONError(ctx.Tr("actions.variables.deletion.failed"))
   105  		return
   106  	}
   107  	ctx.Flash.Success(ctx.Tr("actions.variables.deletion.success"))
   108  	ctx.JSONRedirect(redirectURL)
   109  }
   110  
   111  func ReserveLineBreakForTextarea(input string) string {
   112  	// Since the content is from a form which is a textarea, the line endings are \r\n.
   113  	// It's a standard behavior of HTML.
   114  	// But we want to store them as \n like what GitHub does.
   115  	// And users are unlikely to really need to keep the \r.
   116  	// Other than this, we should respect the original content, even leading or trailing spaces.
   117  	return strings.ReplaceAll(input, "\r\n", "\n")
   118  }