mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-23 16:44:41 +02:00
Merge branch 'main' into lunny/some_refactors
This commit is contained in:
commit
73279fb770
301 files changed
+72595
-201038
No files matched your search
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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...)
|
||||
|
||||
Reference in new issue
Block a user