0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-12 13:22:55 +02:00

Merge branch 'go-gitea:main' into main

This commit is contained in:
Karthik Bhandary 2026-03-20 10:36:17 +05:30 committed by GitHub
commit ce000b0c19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 74 additions and 52 deletions

View File

@ -108,7 +108,9 @@ curl --connect-timeout 10 --silent --show-error --fail --location -O "$binurl{,.
sha256sum -c "${binname}.xz.sha256"
if [[ -z "${ignore_gpg:-}" ]]; then
require gpg
gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
# try to use curl first, it uses standard tcp 443 port and works better behind strict firewall rules
curl -fsSL --connect-timeout 10 "https://keys.openpgp.org/vks/v1/by-fingerprint/7C9E68152594688862D62AF62D9AE806EC1592E2" | gpg --import \
|| gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
gpg --verify "${binname}.xz.asc" "${binname}.xz" || { echo 'Signature does not match'; exit 1; }
fi
rm "${binname}".xz.{sha256,asc}
@ -127,6 +129,8 @@ echo "Creating backup in $giteahome"
giteacmd dump $backupopts
echo "Updating binary at $giteabin"
cp -f "$giteabin" "$giteabin.bak" && mv -f "$binname" "$giteabin"
# Restore SELinux context if applicable (e.g. RHEL/Fedora)
command -v restorecon &>/dev/null && restorecon -v "$giteabin" || true
$service_start
$service_status

2
go.mod
View File

@ -37,6 +37,7 @@ require (
github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20251013092601-6327009efd21
github.com/chi-middleware/proxy v1.1.1
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
github.com/dlclark/regexp2 v1.11.5
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
github.com/dustin/go-humanize v1.0.1
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4
@ -183,7 +184,6 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect

View File

@ -9,7 +9,6 @@ import (
"errors"
"fmt"
"io"
"regexp"
"strings"
"code.gitea.io/gitea/models/db"
@ -24,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"github.com/dlclark/regexp2"
"xorm.io/builder"
)
@ -861,7 +861,7 @@ func GetCodeOwnersFromContent(ctx context.Context, data string) ([]*CodeOwnerRul
}
type CodeOwnerRule struct {
Rule *regexp.Regexp
Rule *regexp2.Regexp // it supports negative lookahead, does better for end users
Negative bool
Users []*user_model.User
Teams []*org_model.Team
@ -877,7 +877,8 @@ func ParseCodeOwnersLine(ctx context.Context, tokens []string) (*CodeOwnerRule,
warnings := make([]string, 0)
rule.Rule, err = regexp.Compile(fmt.Sprintf("^%s$", strings.TrimPrefix(tokens[0], "!")))
expr := fmt.Sprintf("^%s$", strings.TrimPrefix(tokens[0], "!"))
rule.Rule, err = regexp2.Compile(expr, regexp2.None)
if err != nil {
warnings = append(warnings, fmt.Sprintf("incorrect codeowner regexp: %s", err))
return nil, warnings

View File

@ -402,7 +402,7 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
cols += "DROP COLUMN `" + col + "` CASCADE"
}
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err)
return fmt.Errorf("drop table `%s` columns %v: %w", tableName, columnNames, err)
}
case setting.Database.Type.IsMySQL():
// Drop indexes on columns first
@ -430,7 +430,7 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
cols += "DROP COLUMN `" + col + "`"
}
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err)
return fmt.Errorf("drop table `%s` columns %v: %w", tableName, columnNames, err)
}
case setting.Database.Type.IsMSSQL():
cols := ""
@ -444,27 +444,27 @@ func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
tableName, strings.ReplaceAll(cols, "`", "'"))
constraints := make([]string, 0)
if err := sess.SQL(sql).Find(&constraints); err != nil {
return fmt.Errorf("Find constraints: %v", err)
return fmt.Errorf("find constraints: %w", err)
}
for _, constraint := range constraints {
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP CONSTRAINT `%s`", tableName, constraint)); err != nil {
return fmt.Errorf("Drop table `%s` default constraint `%s`: %v", tableName, constraint, err)
return fmt.Errorf("drop table `%s` default constraint `%s`: %w", tableName, constraint, err)
}
}
sql = fmt.Sprintf("SELECT DISTINCT Name FROM sys.indexes INNER JOIN sys.index_columns ON indexes.index_id = index_columns.index_id AND indexes.object_id = index_columns.object_id WHERE indexes.object_id = OBJECT_ID('%[1]s') AND index_columns.column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
tableName, strings.ReplaceAll(cols, "`", "'"))
constraints = make([]string, 0)
if err := sess.SQL(sql).Find(&constraints); err != nil {
return fmt.Errorf("Find constraints: %v", err)
return fmt.Errorf("find constraints: %w", err)
}
for _, constraint := range constraints {
if _, err := sess.Exec(fmt.Sprintf("DROP INDEX `%[2]s` ON `%[1]s`", tableName, constraint)); err != nil {
return fmt.Errorf("Drop index `%[2]s` on `%[1]s`: %v", tableName, constraint, err)
return fmt.Errorf("drop index `%[2]s` on `%[1]s`: %[3]w", tableName, constraint, err)
}
}
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN %s", tableName, cols)); err != nil {
return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err)
return fmt.Errorf("drop table `%s` columns %v: %w", tableName, columnNames, err)
}
default:
log.Fatal("Unrecognized DB")

View File

@ -96,6 +96,7 @@ func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Cont
m[1] -= closing
}
} else if lastChar == ';' {
// exclude HTML entity reference, e.g.: exclude " " from "http://example.com?foo=1 "
i := m[1] - 2
for ; i >= m[0]; i-- {
if util.IsAlphaNumeric(line[i]) {
@ -105,7 +106,7 @@ func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Cont
}
if i != m[1]-2 {
if line[i] == '&' {
m[1] -= m[1] - i
m[1] = i
}
}
}

View File

@ -3644,6 +3644,7 @@
"actions.runners.id": "ID",
"actions.runners.name": "Ainm",
"actions.runners.owner_type": "Cineál",
"actions.runners.availability": "Infhaighteacht",
"actions.runners.description": "Cur síos",
"actions.runners.labels": "Lipéid",
"actions.runners.last_online": "Am Ar Líne Deiridh",
@ -3659,6 +3660,12 @@
"actions.runners.update_runner": "Nuashonrú Athruithe",
"actions.runners.update_runner_success": "Nuashonraíodh an Reathaí",
"actions.runners.update_runner_failed": "Theip ar an reathaí a nuashonrú",
"actions.runners.enable_runner": "Cumasaigh an ritheoir seo",
"actions.runners.enable_runner_success": "Cumasaíodh an rithire go rathúil",
"actions.runners.enable_runner_failed": "Theip ar an rithire a chumasú",
"actions.runners.disable_runner": "Díchumasaigh an ritheoir seo",
"actions.runners.disable_runner_success": "Díchumasaíodh an ritheoir go rathúil",
"actions.runners.disable_runner_failed": "Theip ar an ritheoir a dhíchumasú",
"actions.runners.delete_runner": "Scrios an reathaí seo",
"actions.runners.delete_runner_success": "Scriosadh an reathaí go rathúil",
"actions.runners.delete_runner_failed": "Theip ar an reathaí a scriosadh",

View File

@ -3644,6 +3644,7 @@
"actions.runners.id": "ID",
"actions.runners.name": "이름",
"actions.runners.owner_type": "유형",
"actions.runners.availability": "가용성",
"actions.runners.description": "설명",
"actions.runners.labels": "레이블",
"actions.runners.last_online": "최근 접속 시간",
@ -3659,6 +3660,12 @@
"actions.runners.update_runner": "변경사항 업데이트",
"actions.runners.update_runner_success": "러너가 성공적으로 업데이트됨",
"actions.runners.update_runner_failed": "러너 업데이트 실패함",
"actions.runners.enable_runner": "이 러너를 활성화",
"actions.runners.enable_runner_success": "러너가 성공적으로 활성화됨",
"actions.runners.enable_runner_failed": "러너 활성화 실패함",
"actions.runners.disable_runner": "이 러너를 비활성화",
"actions.runners.disable_runner_success": "러너 비활성화가 성공함",
"actions.runners.disable_runner_failed": "러너 비활성화 실패함",
"actions.runners.delete_runner": "이 러너를 삭제",
"actions.runners.delete_runner_success": "러너가 성공적으로 삭제됨",
"actions.runners.delete_runner_failed": "러너 삭제를 실패함",

View File

@ -88,7 +88,11 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
}
}
func verifyAuth(r *web.Router, authMethods []auth.Method) {
type verifyAuthOptions struct {
afterAuthCallback func(ctx *context.Context, err error)
}
func verifyAuth(r *web.Router, authMethods []auth.Method, opts verifyAuthOptions) {
if setting.Service.EnableReverseProxyAuth {
authMethods = append(authMethods, &auth.ReverseProxy{})
}
@ -97,12 +101,13 @@ func verifyAuth(r *web.Router, authMethods []auth.Method) {
r.AfterRouting(func(ctx *context.Context) {
var err error
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
if err != nil {
ctx.IsSigned = ctx.Doer != nil
if opts.afterAuthCallback != nil {
opts.afterAuthCallback(ctx, err)
} else if err != nil {
log.Error("Failed to verify user: %v", err)
ctx.HTTPError(http.StatusUnauthorized, "Failed to authenticate user")
return
}
ctx.IsSigned = ctx.Doer != nil
})
}
@ -119,7 +124,7 @@ func CommonRoutes() *web.Router {
&nuget.Auth{},
&Auth{},
&chef.Auth{},
})
}, verifyAuthOptions{})
r.Group("/{username}", func() {
r.Group("/alpine", func() {
@ -537,8 +542,15 @@ func ContainerRoutes() *web.Router {
verifyAuth(r, []auth.Method{
&auth.Basic{},
// container auth requires an token, so container.Authenticate issues a Ghost user token for anonymous access
// container auth requires token, so container.Authenticate issues a Ghost user token for anonymous access
&Auth{AllowGhostUser: true},
}, verifyAuthOptions{
afterAuthCallback: func(ctx *context.Context, err error) {
if err != nil {
log.Error("Failed to verify container user: %v", err)
container.APIUnauthorizedError(ctx)
}
},
})
// TODO: Content Discovery / References (not implemented yet)

View File

@ -120,17 +120,19 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
})
}
func apiUnauthorizedError(ctx *context.Context) {
func APIUnauthorizedError(ctx *context.Context) {
// container registry requires that the "/v2" must be in the root, so the sub-path in AppURL should be removed
realmURL := httplib.GuessCurrentHostURL(ctx) + "/v2/token"
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+realmURL+`",service="container_registry",scope="*"`)
// support apple container like: container registry login <gitea-host> -u
ctx.Resp.Header().Add("WWW-Authenticate", `Basic realm="Gitea Container Registry"`)
apiErrorDefined(ctx, errUnauthorized)
}
// ReqContainerAccess is a middleware which checks the current user valid (real user or ghost if anonymous access is enabled)
func ReqContainerAccess(ctx *context.Context) {
if ctx.Doer == nil || (setting.Service.RequireSignInViewStrict && ctx.Doer.IsGhost()) {
apiUnauthorizedError(ctx)
APIUnauthorizedError(ctx)
}
}
@ -156,7 +158,7 @@ func Authenticate(ctx *context.Context) {
packageScope := auth_service.GetAccessScope(ctx.Data)
if u == nil {
if setting.Service.RequireSignInViewStrict {
apiUnauthorizedError(ctx)
APIUnauthorizedError(ctx)
return
}
@ -170,7 +172,7 @@ func Authenticate(ctx *context.Context) {
if err != nil {
log.Error("Error checking access scope: %v", err)
}
apiUnauthorizedError(ctx)
APIUnauthorizedError(ctx)
return
}
}

View File

@ -118,7 +118,7 @@ func SignInOAuthCallback(ctx *context.Context) {
return
}
if err, ok := err.(*go_oauth2.RetrieveError); ok {
ctx.Flash.Error("OAuth2 RetrieveError: "+err.Error(), true)
ctx.Flash.Error("OAuth2 RetrieveError: " + err.Error())
ctx.Redirect(setting.AppSubURL + "/user/login")
return
}

View File

@ -95,7 +95,9 @@ func PullRequestCodeOwnersReview(ctx context.Context, pr *issues_model.PullReque
uniqTeams := make(map[string]*org_model.Team)
for _, rule := range rules {
for _, f := range changedFiles {
if (rule.Rule.MatchString(f) && !rule.Negative) || (!rule.Rule.MatchString(f) && rule.Negative) {
shouldMatch := !rule.Negative
matched, _ := rule.Rule.MatchString(f) // err only happens when timeouts, any error can be considered as not matched
if matched == shouldMatch {
for _, u := range rule.Users {
uniqUsers[u.ID] = u
}

View File

@ -61,33 +61,18 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
return nil, err
}
apiURL := repo.APIURL()
apiURLLen := len(apiURL)
objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName)
hashLen := objectFormat.FullLength()
const gitBlobsPath = "/git/blobs/"
blobURL := make([]byte, apiURLLen+hashLen+len(gitBlobsPath))
copy(blobURL, apiURL)
copy(blobURL[apiURLLen:], []byte(gitBlobsPath))
const gitTreePath = "/git/trees/"
treeURL := make([]byte, apiURLLen+hashLen+len(gitTreePath))
copy(treeURL, apiURL)
copy(treeURL[apiURLLen:], []byte(gitTreePath))
// copyPos is at the start of the hash
copyPos := len(treeURL) - hashLen
blobURLBase := apiURL + "/git/blobs/"
treeURLBase := apiURL + "/git/trees/"
if perPage <= 0 || perPage > setting.API.DefaultGitTreesPerPage {
perPage = setting.API.DefaultGitTreesPerPage
}
if page <= 0 {
page = 1
}
page = max(page, 1)
tree.Page = page
tree.TotalCount = len(entries)
rangeStart := perPage * (page - 1)
if rangeStart >= len(entries) {
rangeStart := perPage * (page - 1) // int might overflow
if rangeStart < 0 || rangeStart >= len(entries) {
return tree, nil
}
rangeEnd := min(rangeStart+perPage, len(entries))
@ -103,16 +88,14 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
tree.Entries[i].SHA = entries[e].ID.String()
if entries[e].IsDir() {
copy(treeURL[copyPos:], entries[e].ID.String())
tree.Entries[i].URL = string(treeURL)
tree.Entries[i].URL = treeURLBase + entries[e].ID.String()
} else if entries[e].IsSubModule() {
// In Github Rest API Version=2022-11-28, if a tree entry is a submodule,
// In GitHub Rest API Version=2022-11-28, if a tree entry is a submodule,
// its url will be returned as an empty string.
// So the URL will be set to "" here.
tree.Entries[i].URL = ""
} else {
copy(blobURL[copyPos:], entries[e].ID.String())
tree.Entries[i].URL = string(blobURL)
tree.Entries[i].URL = blobURLBase + entries[e].ID.String()
}
}
return tree, nil

View File

@ -88,7 +88,10 @@ func TestPackageContainer(t *testing.T) {
Token string `json:"token"`
}
defaultAuthenticateValues := []string{`Bearer realm="` + setting.AppURL + `v2/token",service="container_registry",scope="*"`}
defaultAuthenticateValues := []string{
`Bearer realm="` + setting.AppURL + `v2/token",service="container_registry",scope="*"`,
`Basic realm="Gitea Container Registry"`,
}
t.Run("Anonymous", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()