0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-14 00:01:05 +02:00

Fix repo init README EOL (#37388) (#37399)

Backport #37388 by @wxiaoguang

Fix #27120

By the way, refactor ReserveLineBreakForTextarea to NormalizeStringEOL

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Giteabot 2026-04-24 07:33:25 +08:00 committed by GitHub
parent 55a6cfe79b
commit 5d7768f34c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 14 additions and 11 deletions

View File

@ -255,11 +255,13 @@ func EnumValue[T comparable](val EnumConst[T]) (ret T, valid bool) {
return enums[0], false 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. // Since the content is from a form which is a textarea, the line endings are \r\n.
// It's a standard behavior of HTML. // It's a standard behavior of HTML.
// But we want to store them as \n like what GitHub does. // But in most cases, we only want "\n" for EOL
// And users are unlikely to really need to keep the \r. // * 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. // 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)))
} }

View File

@ -175,9 +175,9 @@ func TestToTitleCase(t *testing.T) {
assert.Equal(t, `Foo Bar Baz`, ToTitleCase(`FOO BAR BAZ`)) assert.Equal(t, `Foo Bar Baz`, ToTitleCase(`FOO BAR BAZ`))
} }
func TestReserveLineBreakForTextarea(t *testing.T) { func TestNormalizeStringEOL(t *testing.T) {
assert.Equal(t, "test\ndata", ReserveLineBreakForTextarea("test\r\ndata")) assert.Equal(t, "test\ndata", NormalizeStringEOL("test\r\ndata"))
assert.Equal(t, "test\ndata\n", ReserveLineBreakForTextarea("test\r\ndata\r\n")) assert.Equal(t, " test\ndata\n ", NormalizeStringEOL(" test\rdata\r "))
} }
func TestOptionalArg(t *testing.T) { func TestOptionalArg(t *testing.T) {

View File

@ -29,7 +29,7 @@ func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) {
func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) { func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL string) {
form := web.GetForm(ctx).(*forms.AddSecretForm) 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 { if err != nil {
log.Error("CreateOrUpdateSecret failed: %v", err) log.Error("CreateOrUpdateSecret failed: %v", err)
ctx.JSONError(ctx.Tr("secrets.save_failed")) ctx.JSONError(ctx.Tr("secrets.save_failed"))

View File

@ -16,7 +16,7 @@ func CreateVariable(ctx context.Context, ownerID, repoID int64, name, data, desc
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -29,7 +29,7 @@ func UpdateVariableNameData(ctx context.Context, variable *actions_model.ActionV
return false, err return false, err
} }
variable.Data = util.ReserveLineBreakForTextarea(variable.Data) variable.Data = util.NormalizeStringEOL(variable.Data)
return actions_model.UpdateVariableCols(ctx, variable, "name", "data", "description") return actions_model.UpdateVariableCols(ctx, variable, "name", "data", "description")
} }

View File

@ -31,6 +31,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates/vars" "code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/util"
) )
// CreateRepoOptions contains the create repository options // 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 */) cloneLink := repo.CloneLink(ctx, nil /* no doer so do not generate user-related SSH link */)
match := map[string]string{ match := map[string]string{
"Name": repo.Name, "Name": repo.Name,
"Description": repo.Description, "Description": util.NormalizeStringEOL(repo.Description),
"CloneURL.SSH": cloneLink.SSH, "CloneURL.SSH": cloneLink.SSH,
"CloneURL.HTTPS": cloneLink.HTTPS, "CloneURL.HTTPS": cloneLink.HTTPS,
"OwnerName": repo.OwnerName, "OwnerName": repo.OwnerName,