From 5d7768f34ca7d100823a2ced855c288409478690 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Fri, 24 Apr 2026 07:33:25 +0800 Subject: [PATCH] Fix repo init README EOL (#37388) (#37399) Backport #37388 by @wxiaoguang Fix #27120 By the way, refactor ReserveLineBreakForTextarea to NormalizeStringEOL Co-authored-by: wxiaoguang --- modules/util/util.go | 10 ++++++---- modules/util/util_test.go | 6 +++--- routers/web/shared/secrets/secrets.go | 2 +- services/actions/variables.go | 4 ++-- services/repository/create.go | 3 ++- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/modules/util/util.go b/modules/util/util.go index 7d1343f20d..1701f34e0b 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -255,11 +255,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 91edecd922..d8caabfb16 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -175,9 +175,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 29f4e9520d..c8842a67e9 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 57e6af1d9b..3593caa2c5 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 a8b57b6707..b0b1f4e7c7 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,