diff --git a/modules/util/util.go b/modules/util/util.go index ec118aaf0de37..04d0fb584d5d3 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -281,11 +281,13 @@ func EnumValue[T comparable](val EnumConst[T]) (ret T, valid bool) { return enums[0], false } -func ReserveLineBreakForTextarea(input string) string { +func NormalizeStringEOL(input string) string { // Since the content is from a form which is a textarea, the line endings are \r\n. // It's a standard behavior of HTML. - // But we want to store them as \n like what GitHub does. - // And users are unlikely to really need to keep the \r. + // But in most cases, we only want "\n" for EOL + // * Text files: use "\n" by default because "\r\n" sometimes doesn't work in POSIX + // * Actions values: store them as "\n" like what GitHub does. + // And users are unlikely to really need the "\r". // Other than this, we should respect the original content, even leading or trailing spaces. - return strings.ReplaceAll(input, "\r\n", "\n") + return UnsafeBytesToString(NormalizeEOL(UnsafeStringToBytes(input))) } diff --git a/modules/util/util_test.go b/modules/util/util_test.go index 9decf90f676d9..7dbb14e374b87 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -167,9 +167,9 @@ func TestToTitleCase(t *testing.T) { assert.Equal(t, `Foo Bar Baz`, ToTitleCase(`FOO BAR BAZ`)) } -func TestReserveLineBreakForTextarea(t *testing.T) { - assert.Equal(t, "test\ndata", ReserveLineBreakForTextarea("test\r\ndata")) - assert.Equal(t, "test\ndata\n", ReserveLineBreakForTextarea("test\r\ndata\r\n")) +func TestNormalizeStringEOL(t *testing.T) { + assert.Equal(t, "test\ndata", NormalizeStringEOL("test\r\ndata")) + assert.Equal(t, " test\ndata\n ", NormalizeStringEOL(" test\rdata\r ")) } func TestOptionalArg(t *testing.T) { diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go index 29f4e9520dc27..c8842a67e9023 100644 --- a/routers/web/shared/secrets/secrets.go +++ b/routers/web/shared/secrets/secrets.go @@ -29,7 +29,7 @@ func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) { func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) { form := web.GetForm(ctx).(*forms.AddSecretForm) - s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data), form.Description) + s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.NormalizeStringEOL(form.Data), form.Description) if err != nil { log.Error("CreateOrUpdateSecret failed: %v", err) ctx.JSONError(ctx.Tr("secrets.save_failed")) diff --git a/services/actions/variables.go b/services/actions/variables.go index 57e6af1d9ba61..3593caa2c5e19 100644 --- a/services/actions/variables.go +++ b/services/actions/variables.go @@ -16,7 +16,7 @@ func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data, desc return nil, err } - v, err := actions_model.InsertVariable(ctx, ownerID, repoID, name, util.ReserveLineBreakForTextarea(data), description) + v, err := actions_model.InsertVariable(ctx, ownerID, repoID, name, util.NormalizeStringEOL(data), description) if err != nil { return nil, err } @@ -29,7 +29,7 @@ func UpdateVariableNameData(ctx context.Context, variable *actions_model.ActionV return false, err } - variable.Data = util.ReserveLineBreakForTextarea(variable.Data) + variable.Data = util.NormalizeStringEOL(variable.Data) return actions_model.UpdateVariableCols(ctx, variable, "name", "data", "description") } diff --git a/services/repository/create.go b/services/repository/create.go index a8b57b67071e6..b0b1f4e7c7693 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -31,6 +31,7 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/templates/vars" + "code.gitea.io/gitea/modules/util" ) // CreateRepoOptions contains the create repository options @@ -85,7 +86,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir cloneLink := repo.CloneLink(ctx, nil /* no doer so do not generate user-related SSH link */) match := map[string]string{ "Name": repo.Name, - "Description": repo.Description, + "Description": util.NormalizeStringEOL(repo.Description), "CloneURL.SSH": cloneLink.SSH, "CloneURL.HTTPS": cloneLink.HTTPS, "OwnerName": repo.OwnerName,