Merge branch 'main' into lunny/some_refactors

This commit is contained in:
Lunny Xiao committed 2025-12-28 11:27:42 -08:00
commit 73279fb770
301 files changed
+72595 -201038

No files matched your search

+19 -5
View File
@@ -9,24 +9,38 @@ package git
import (
"io"
"code.gitea.io/gitea/modules/log"
"github.com/go-git/go-git/v5/plumbing"
)
// Blob represents a Git object.
type Blob struct {
ID ObjectID
ID ObjectID
repo *Repository
name string
}
gogitEncodedObj plumbing.EncodedObject
name string
func (b *Blob) gogitEncodedObj() (plumbing.EncodedObject, error) {
return b.repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, plumbing.Hash(b.ID.RawValue()))
}
// DataAsync gets a ReadCloser for the contents of a blob without reading it all.
// Calling the Close function on the result will discard all unread output.
func (b *Blob) DataAsync() (io.ReadCloser, error) {
return b.gogitEncodedObj.Reader()
obj, err := b.gogitEncodedObj()
if err != nil {
return nil, err
}
return obj.Reader()
}
// Size returns the uncompressed size of the blob
func (b *Blob) Size() int64 {
return b.gogitEncodedObj.Size()
obj, err := b.gogitEncodedObj()
if err != nil {
log.Error("Error getting gogit encoded object for blob %s(%s): %v", b.name, b.ID.String(), err)
return 0
}
return obj.Size()
}
+3 -3
View File
@@ -186,7 +186,7 @@ func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error {
// PushOptions options when push to remote
type PushOptions struct {
Remote string
LocalBranch string
LocalRefName string
Branch string
Force bool
ForceWithLease string
@@ -209,8 +209,8 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
remoteBranchArgs := []string{opts.Remote}
if len(opts.Branch) > 0 {
var refspec string
if opts.LocalBranch != "" {
refspec = fmt.Sprintf("%s:%s", opts.LocalBranch, opts.Branch)
if opts.LocalRefName != "" {
refspec = fmt.Sprintf("%s:%s", opts.LocalRefName, opts.Branch)
} else {
refspec = opts.Branch
}
+7 -1
View File
@@ -9,5 +9,11 @@ func (repo *Repository) GetBlob(idStr string) (*Blob, error) {
if err != nil {
return nil, err
}
return repo.getBlob(id)
if id.IsZero() {
return nil, ErrNotExist{id.String(), ""}
}
return &Blob{
ID: id,
repo: repo,
}, nil
}
-22
View File
@@ -1,22 +0,0 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build gogit
package git
import (
"github.com/go-git/go-git/v5/plumbing"
)
func (repo *Repository) getBlob(id ObjectID) (*Blob, error) {
encodedObj, err := repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, plumbing.Hash(id.RawValue()))
if err != nil {
return nil, ErrNotExist{id.String(), ""}
}
return &Blob{
ID: id,
gogitEncodedObj: encodedObj,
}, nil
}
-16
View File
@@ -1,16 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !gogit
package git
func (repo *Repository) getBlob(id ObjectID) (*Blob, error) {
if id.IsZero() {
return nil, ErrNotExist{id.String(), ""}
}
return &Blob{
ID: id,
repo: repo,
}, nil
}
+3 -8
View File
@@ -53,14 +53,9 @@ func (te *TreeEntry) Size() int64 {
// Blob returns the blob object the entry
func (te *TreeEntry) Blob() *Blob {
encodedObj, err := te.ptree.repo.gogitRepo.Storer.EncodedObject(plumbing.AnyObject, te.toGogitTreeEntry().Hash)
if err != nil {
return nil
}
return &Blob{
ID: te.ID,
gogitEncodedObj: encodedObj,
name: te.Name(),
ID: te.ID,
repo: te.ptree.repo,
name: te.Name(),
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ func (r *stripRenderer) Render(w io.Writer, source []byte, doc ast.Node) error {
coalesce := prevSibIsText
r.processString(
w,
v.Text(source), //nolint:staticcheck // Text is deprecated
v.Value(source),
coalesce)
if v.SoftLineBreak() {
r.doubleSpace(w)
+24 -6
View File
@@ -248,7 +248,7 @@ func FindAllIssueReferencesMarkdown(content string) []IssueReference {
func findAllIssueReferencesMarkdown(content string) []*rawReference {
bcontent, links := mdstripper.StripMarkdownBytes([]byte(content))
return findAllIssueReferencesBytes(bcontent, links)
return findAllIssueReferencesBytes(bcontent, links, []byte(content))
}
func convertFullHTMLReferencesToShortRefs(re *regexp.Regexp, contentBytes *[]byte) {
@@ -326,7 +326,7 @@ func FindAllIssueReferences(content string) []IssueReference {
} else {
log.Debug("No GiteaIssuePullPattern pattern")
}
return rawToIssueReferenceList(findAllIssueReferencesBytes(contentBytes, []string{}))
return rawToIssueReferenceList(findAllIssueReferencesBytes(contentBytes, []string{}, nil))
}
// FindRenderizableReferenceNumeric returns the first unvalidated reference found in a string.
@@ -406,7 +406,8 @@ func FindRenderizableReferenceAlphanumeric(content string) *RenderizableReferenc
}
// FindAllIssueReferencesBytes returns a list of unvalidated references found in a byte slice.
func findAllIssueReferencesBytes(content []byte, links []string) []*rawReference {
// originalContent is optional and used to detect closing/reopening keywords for URL references.
func findAllIssueReferencesBytes(content []byte, links []string, originalContent []byte) []*rawReference {
ret := make([]*rawReference, 0, 10)
pos := 0
@@ -470,10 +471,27 @@ func findAllIssueReferencesBytes(content []byte, links []string) []*rawReference
default:
continue
}
// Note: closing/reopening keywords not supported with URLs
bytes := []byte(parts[1] + "/" + parts[2] + sep + parts[4])
if ref := getCrossReference(bytes, 0, len(bytes), true, false); ref != nil {
refBytes := []byte(parts[1] + "/" + parts[2] + sep + parts[4])
if ref := getCrossReference(refBytes, 0, len(refBytes), true, false); ref != nil {
ref.refLocation = nil
// Detect closing/reopening keywords by finding the URL position in original content
if originalContent != nil {
if idx := bytes.Index(originalContent, []byte(link)); idx > 0 {
// For markdown links [text](url), find the opening bracket before the URL
// to properly detect keywords like "closes [text](url)"
searchStart := idx
if idx >= 2 && originalContent[idx-1] == '(' {
// Find the matching '[' for this markdown link
bracketIdx := bytes.LastIndex(originalContent[:idx-1], []byte{'['})
if bracketIdx >= 0 {
searchStart = bracketIdx
}
}
action, location := findActionKeywords(originalContent, searchStart)
ref.action = action
ref.actionLocation = location
}
}
ret = append(ret, ref)
}
}
+56
View File
@@ -227,6 +227,62 @@ func TestFindAllIssueReferences(t *testing.T) {
testFixtures(t, fixtures, "default")
// Test closing/reopening keywords with URLs (issue #27549)
// Uses the same AppURL as testFixtures (https://gitea.com:3000/)
urlFixtures := []testFixture{
{
"Closes [this issue](https://gitea.com:3000/user/repo/issues/123)",
[]testResult{
{123, "user", "repo", "123", false, XRefActionCloses, nil, &RefSpan{Start: 0, End: 6}, ""},
},
},
{
"This fixes [#456](https://gitea.com:3000/org/project/issues/456)",
[]testResult{
{456, "org", "project", "456", false, XRefActionCloses, nil, &RefSpan{Start: 5, End: 10}, ""},
},
},
{
"Reopens [PR](https://gitea.com:3000/owner/repo/pulls/789)",
[]testResult{
{789, "owner", "repo", "789", true, XRefActionReopens, nil, &RefSpan{Start: 0, End: 7}, ""},
},
},
{
"See [issue](https://gitea.com:3000/user/repo/issues/100) but closes [another](https://gitea.com:3000/user/repo/issues/200)",
[]testResult{
{100, "user", "repo", "100", false, XRefActionNone, nil, nil, ""},
{200, "user", "repo", "200", false, XRefActionCloses, nil, &RefSpan{Start: 61, End: 67}, ""},
},
},
}
testFixtures(t, urlFixtures, "url-keywords")
// Test bare URLs (not markdown links) with closing keywords
// These use FindAllIssueReferences (non-markdown) which converts full URLs to short refs first
setting.AppURL = "https://gitea.com:3000/"
bareURLTests := []struct {
name string
input string
expected XRefAction
}{
{"Fixes bare URL", "Fixes https://gitea.com:3000/org/project/issues/456", XRefActionCloses},
{"Fixes with colon", "Fixes: https://gitea.com:3000/org/project/issues/456", XRefActionCloses},
{"Closes bare URL", "Closes https://gitea.com:3000/user/repo/issues/123", XRefActionCloses},
{"Closes with colon", "Closes: https://gitea.com:3000/user/repo/issues/123", XRefActionCloses},
}
for _, tt := range bareURLTests {
t.Run(tt.name, func(t *testing.T) {
refs := FindAllIssueReferences(tt.input)
assert.Len(t, refs, 1, "Expected 1 reference for: %s", tt.input)
if len(refs) > 0 {
assert.Equal(t, tt.expected, refs[0].Action, "Expected action %v for: %s", tt.expected, tt.input)
}
})
}
type alnumFixture struct {
input string
issue string
+1 -1
View File
@@ -255,7 +255,7 @@ func newMarkupRenderer(name string, sec ConfigSection) {
}
// ATTENTION! at the moment, only a safe set like "allow-scripts" are allowed for sandbox mode.
// "allow-same-origin" should never be used, it leads to XSS attack, and it makes the JS in iframe can access parent window's config and CSRF token
// "allow-same-origin" should NEVER be used, it leads to XSS attack: makes the JS in iframe can access parent window's config and send requests with user's credentials.
renderContentSandbox := sec.Key("RENDER_CONTENT_SANDBOX").MustString("allow-scripts allow-popups")
if renderContentSandbox == "disabled" {
renderContentSandbox = ""
+1 -1
View File
@@ -133,7 +133,7 @@ func loadOAuth2From(rootCfg ConfigProvider) {
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
// Because this secret is also used as GeneralTokenSigningSecret (as a quick not-that-breaking fix for some legacy problems).
// Including: CSRF token, account validation token, etc ...
// Including: account validation token, etc ...
// In main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
if InstallLock {
-3
View File
@@ -36,8 +36,6 @@ var (
PasswordCheckPwn bool
SuccessfulTokensCacheSize int
DisableQueryAuthToken bool
CSRFCookieName = "_csrf"
CSRFCookieHTTPOnly = true
RecordUserSignupMetadata = false
TwoFactorAuthEnforced = false
)
@@ -139,7 +137,6 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
log.Fatal("The provided password hash algorithm was invalid: %s", sec.Key("PASSWORD_HASH_ALGO").MustString(""))
}
CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)
PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
+6 -7
View File
@@ -131,16 +131,15 @@ func (l *locale) TrString(trKey string, trArgs ...any) string {
var format string
idx, ok := l.store.trKeyToIdxMap[trKey]
if ok {
if msg, ok := l.idxToMsgMap[idx]; ok {
format = msg // use the found translation
} else if def, ok := l.store.localeMap[l.store.defaultLang]; ok {
// try to use default locale's translation
if msg, ok := def.idxToMsgMap[idx]; ok {
format = msg
format = l.idxToMsgMap[idx]
if format == "" { // missing translation in this locale, fallback to default
if def, ok := l.store.localeMap[l.store.defaultLang]; ok {
// try to use default locale's translation
format = def.idxToMsgMap[idx]
}
}
}
if format == "" {
if format == "" { // still missing, use the key itself
format = html.EscapeString(trKey)
}
msg, err := Format(format, trArgs...)