Skip to content

Commit cc1ec32

Browse files
committed
Merge remote-tracking branch 'giteaofficial/main'
* giteaofficial/main: [skip ci] Updated translations via Crowdin Apply to become a maintainer (go-gitea#30884) Refactor AppURL usage (go-gitea#30885) Move database operations of merging a pull request to post receive hook and add a transaction (go-gitea#30805) Fix missing migrate actions artifacts (go-gitea#30874)
2 parents 36f7bf4 + f1b0729 commit cc1ec32

File tree

24 files changed

+316
-73
lines changed

24 files changed

+316
-73
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ kerwin612 <[email protected]> (@kerwin612)
6161
Gary Wang <[email protected]> (@BLumia)
6262
Tim-Niclas Oelschläger <[email protected]> (@zokkis)
6363
Yu Liu <[email protected]> (@HEREYUA)
64+
Kemal Zebari <[email protected]> (@kemzeb)

cmd/hook.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ Gitea or set your environment appropriately.`, "")
338338
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
339339
repoName := os.Getenv(repo_module.EnvRepoName)
340340
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
341+
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
341342
pusherName := os.Getenv(repo_module.EnvPusherName)
342343

343344
hookOptions := private.HookOptions{
@@ -347,6 +348,8 @@ Gitea or set your environment appropriately.`, "")
347348
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
348349
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
349350
GitPushOptions: pushOptions(),
351+
PullRequestID: prID,
352+
PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
350353
}
351354
oldCommitIDs := make([]string, hookBatchSize)
352355
newCommitIDs := make([]string, hookBatchSize)

cmd/migrate_storage.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ var CmdMigrateStorage = &cli.Command{
3434
Name: "type",
3535
Aliases: []string{"t"},
3636
Value: "",
37-
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log'",
37+
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts",
3838
},
3939
&cli.StringFlag{
4040
Name: "storage",
@@ -160,6 +160,13 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
160160
})
161161
}
162162

163+
func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
164+
return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
165+
_, err := storage.Copy(dstStorage, artifact.ArtifactPath, storage.ActionsArtifacts, artifact.ArtifactPath)
166+
return err
167+
})
168+
}
169+
163170
func runMigrateStorage(ctx *cli.Context) error {
164171
stdCtx, cancel := installSignals()
165172
defer cancel()
@@ -223,13 +230,14 @@ func runMigrateStorage(ctx *cli.Context) error {
223230
}
224231

225232
migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{
226-
"attachments": migrateAttachments,
227-
"lfs": migrateLFS,
228-
"avatars": migrateAvatars,
229-
"repo-avatars": migrateRepoAvatars,
230-
"repo-archivers": migrateRepoArchivers,
231-
"packages": migratePackages,
232-
"actions-log": migrateActionsLog,
233+
"attachments": migrateAttachments,
234+
"lfs": migrateLFS,
235+
"avatars": migrateAvatars,
236+
"repo-avatars": migrateRepoAvatars,
237+
"repo-archivers": migrateRepoArchivers,
238+
"packages": migratePackages,
239+
"actions-log": migrateActionsLog,
240+
"actions-artifacts": migrateActionsArtifacts,
233241
}
234242

235243
tp := strings.ToLower(ctx.String("type"))

models/repo/avatar.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import (
99
"image/png"
1010
"io"
1111
"net/url"
12-
"strings"
1312

1413
"code.gitea.io/gitea/models/db"
1514
"code.gitea.io/gitea/modules/avatar"
15+
"code.gitea.io/gitea/modules/httplib"
1616
"code.gitea.io/gitea/modules/log"
1717
"code.gitea.io/gitea/modules/setting"
1818
"code.gitea.io/gitea/modules/storage"
@@ -84,13 +84,7 @@ func (repo *Repository) relAvatarLink(ctx context.Context) string {
8484
return setting.AppSubURL + "/repo-avatars/" + url.PathEscape(repo.Avatar)
8585
}
8686

87-
// AvatarLink returns a link to the repository's avatar.
87+
// AvatarLink returns the full avatar url with http host. TODO: refactor it to a relative URL, but it is still used in API response at the moment
8888
func (repo *Repository) AvatarLink(ctx context.Context) string {
89-
link := repo.relAvatarLink(ctx)
90-
// we only prepend our AppURL to our known (relative, internal) avatar link to get an absolute URL
91-
if strings.HasPrefix(link, "/") && !strings.HasPrefix(link, "//") {
92-
return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL)[1:]
93-
}
94-
// otherwise, return the link as it is
95-
return link
89+
return httplib.MakeAbsoluteURL(ctx, repo.relAvatarLink(ctx))
9690
}

models/user/avatar.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import (
99
"fmt"
1010
"image/png"
1111
"io"
12-
"strings"
1312

1413
"code.gitea.io/gitea/models/avatars"
1514
"code.gitea.io/gitea/models/db"
1615
"code.gitea.io/gitea/modules/avatar"
16+
"code.gitea.io/gitea/modules/httplib"
1717
"code.gitea.io/gitea/modules/log"
1818
"code.gitea.io/gitea/modules/setting"
1919
"code.gitea.io/gitea/modules/storage"
@@ -89,13 +89,9 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
8989
return avatars.GenerateEmailAvatarFastLink(ctx, u.AvatarEmail, size)
9090
}
9191

92-
// AvatarLink returns the full avatar link with http host
92+
// AvatarLink returns the full avatar url with http host. TODO: refactor it to a relative URL, but it is still used in API response at the moment
9393
func (u *User) AvatarLink(ctx context.Context) string {
94-
link := u.AvatarLinkWithSize(ctx, 0)
95-
if !strings.HasPrefix(link, "//") && !strings.Contains(link, "://") {
96-
return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL+"/")
97-
}
98-
return link
94+
return httplib.MakeAbsoluteURL(ctx, u.AvatarLinkWithSize(ctx, 0))
9995
}
10096

10197
// IsUploadAvatarChanged returns true if the current user's avatar would be changed with the provided data

modules/httplib/url.go

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@
44
package httplib
55

66
import (
7+
"context"
8+
"net/http"
79
"net/url"
810
"strings"
911

1012
"code.gitea.io/gitea/modules/setting"
1113
"code.gitea.io/gitea/modules/util"
1214
)
1315

16+
type RequestContextKeyStruct struct{}
17+
18+
var RequestContextKey = RequestContextKeyStruct{}
19+
1420
func urlIsRelative(s string, u *url.URL) bool {
1521
// Unfortunately browsers consider a redirect Location with preceding "//", "\\", "/\" and "\/" as meaning redirect to "http(s)://REST_OF_PATH"
1622
// Therefore we should ignore these redirect locations to prevent open redirects
@@ -26,7 +32,56 @@ func IsRelativeURL(s string) bool {
2632
return err == nil && urlIsRelative(s, u)
2733
}
2834

29-
func IsCurrentGiteaSiteURL(s string) bool {
35+
func guessRequestScheme(req *http.Request, def string) string {
36+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
37+
if s := req.Header.Get("X-Forwarded-Proto"); s != "" {
38+
return s
39+
}
40+
if s := req.Header.Get("X-Forwarded-Protocol"); s != "" {
41+
return s
42+
}
43+
if s := req.Header.Get("X-Url-Scheme"); s != "" {
44+
return s
45+
}
46+
if s := req.Header.Get("Front-End-Https"); s != "" {
47+
return util.Iif(s == "on", "https", "http")
48+
}
49+
if s := req.Header.Get("X-Forwarded-Ssl"); s != "" {
50+
return util.Iif(s == "on", "https", "http")
51+
}
52+
return def
53+
}
54+
55+
func guessForwardedHost(req *http.Request) string {
56+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host
57+
return req.Header.Get("X-Forwarded-Host")
58+
}
59+
60+
// GuessCurrentAppURL tries to guess the current full URL by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
61+
func GuessCurrentAppURL(ctx context.Context) string {
62+
req, ok := ctx.Value(RequestContextKey).(*http.Request)
63+
if !ok {
64+
return setting.AppURL
65+
}
66+
if host := guessForwardedHost(req); host != "" {
67+
// if it is behind a reverse proxy, use "https" as default scheme in case the site admin forgets to set the correct forwarded-protocol headers
68+
return guessRequestScheme(req, "https") + "://" + host + setting.AppSubURL + "/"
69+
} else if req.Host != "" {
70+
// if it is not behind a reverse proxy, use the scheme from config options, meanwhile use "https" as much as possible
71+
defaultScheme := util.Iif(setting.Protocol == "http", "http", "https")
72+
return guessRequestScheme(req, defaultScheme) + "://" + req.Host + setting.AppSubURL + "/"
73+
}
74+
return setting.AppURL
75+
}
76+
77+
func MakeAbsoluteURL(ctx context.Context, s string) string {
78+
if IsRelativeURL(s) {
79+
return GuessCurrentAppURL(ctx) + strings.TrimPrefix(s, "/")
80+
}
81+
return s
82+
}
83+
84+
func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {
3085
u, err := url.Parse(s)
3186
if err != nil {
3287
return false
@@ -45,5 +100,6 @@ func IsCurrentGiteaSiteURL(s string) bool {
45100
if u.Path == "" {
46101
u.Path = "/"
47102
}
48-
return strings.HasPrefix(strings.ToLower(u.String()), strings.ToLower(setting.AppURL))
103+
urlLower := strings.ToLower(u.String())
104+
return strings.HasPrefix(urlLower, strings.ToLower(setting.AppURL)) || strings.HasPrefix(urlLower, strings.ToLower(GuessCurrentAppURL(ctx)))
49105
}

modules/httplib/url_test.go

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
package httplib
55

66
import (
7+
"context"
8+
"net/http"
79
"testing"
810

911
"code.gitea.io/gitea/modules/setting"
@@ -37,9 +39,44 @@ func TestIsRelativeURL(t *testing.T) {
3739
}
3840
}
3941

42+
func TestMakeAbsoluteURL(t *testing.T) {
43+
defer test.MockVariableValue(&setting.Protocol, "http")()
44+
defer test.MockVariableValue(&setting.AppURL, "http://the-host/sub/")()
45+
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
46+
47+
ctx := context.Background()
48+
assert.Equal(t, "http://the-host/sub/", MakeAbsoluteURL(ctx, ""))
49+
assert.Equal(t, "http://the-host/sub/foo", MakeAbsoluteURL(ctx, "foo"))
50+
assert.Equal(t, "http://the-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
51+
assert.Equal(t, "http://other/foo", MakeAbsoluteURL(ctx, "http://other/foo"))
52+
53+
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
54+
Host: "user-host",
55+
})
56+
assert.Equal(t, "http://user-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
57+
58+
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
59+
Host: "user-host",
60+
Header: map[string][]string{
61+
"X-Forwarded-Host": {"forwarded-host"},
62+
},
63+
})
64+
assert.Equal(t, "https://forwarded-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
65+
66+
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
67+
Host: "user-host",
68+
Header: map[string][]string{
69+
"X-Forwarded-Host": {"forwarded-host"},
70+
"X-Forwarded-Proto": {"https"},
71+
},
72+
})
73+
assert.Equal(t, "https://forwarded-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
74+
}
75+
4076
func TestIsCurrentGiteaSiteURL(t *testing.T) {
4177
defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/sub/")()
4278
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
79+
ctx := context.Background()
4380
good := []string{
4481
"?key=val",
4582
"/sub",
@@ -50,7 +87,7 @@ func TestIsCurrentGiteaSiteURL(t *testing.T) {
5087
"http://localhost:3000/sub/",
5188
}
5289
for _, s := range good {
53-
assert.True(t, IsCurrentGiteaSiteURL(s), "good = %q", s)
90+
assert.True(t, IsCurrentGiteaSiteURL(ctx, s), "good = %q", s)
5491
}
5592
bad := []string{
5693
".",
@@ -64,13 +101,23 @@ func TestIsCurrentGiteaSiteURL(t *testing.T) {
64101
"http://other/",
65102
}
66103
for _, s := range bad {
67-
assert.False(t, IsCurrentGiteaSiteURL(s), "bad = %q", s)
104+
assert.False(t, IsCurrentGiteaSiteURL(ctx, s), "bad = %q", s)
68105
}
69106

70107
setting.AppURL = "http://localhost:3000/"
71108
setting.AppSubURL = ""
72-
assert.False(t, IsCurrentGiteaSiteURL("//"))
73-
assert.False(t, IsCurrentGiteaSiteURL("\\\\"))
74-
assert.False(t, IsCurrentGiteaSiteURL("http://localhost"))
75-
assert.True(t, IsCurrentGiteaSiteURL("http://localhost:3000?key=val"))
109+
assert.False(t, IsCurrentGiteaSiteURL(ctx, "//"))
110+
assert.False(t, IsCurrentGiteaSiteURL(ctx, "\\\\"))
111+
assert.False(t, IsCurrentGiteaSiteURL(ctx, "http://localhost"))
112+
assert.True(t, IsCurrentGiteaSiteURL(ctx, "http://localhost:3000?key=val"))
113+
114+
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
115+
Host: "user-host",
116+
Header: map[string][]string{
117+
"X-Forwarded-Host": {"forwarded-host"},
118+
"X-Forwarded-Proto": {"https"},
119+
},
120+
})
121+
assert.True(t, IsCurrentGiteaSiteURL(ctx, "http://localhost:3000"))
122+
assert.True(t, IsCurrentGiteaSiteURL(ctx, "https://forwarded-host"))
76123
}

modules/markup/html_codepreview.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosSt
4242
CommitID: node.Data[m[6]:m[7]],
4343
FilePath: node.Data[m[8]:m[9]],
4444
}
45-
if !httplib.IsCurrentGiteaSiteURL(opts.FullURL) {
45+
if !httplib.IsCurrentGiteaSiteURL(ctx.Ctx, opts.FullURL) {
4646
return 0, 0, "", nil
4747
}
4848
u, err := url.Parse(opts.FilePath)

modules/private/hook.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"code.gitea.io/gitea/modules/git"
1414
"code.gitea.io/gitea/modules/optional"
15+
"code.gitea.io/gitea/modules/repository"
1516
"code.gitea.io/gitea/modules/setting"
1617
)
1718

@@ -54,6 +55,7 @@ type HookOptions struct {
5455
GitQuarantinePath string
5556
GitPushOptions GitPushOptions
5657
PullRequestID int64
58+
PushTrigger repository.PushTrigger
5759
DeployKeyID int64 // if the pusher is a DeployKey, then UserID is the repo's org user.
5860
IsWiki bool
5961
ActionPerm int

modules/repository/env.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,19 @@ const (
2525
EnvKeyID = "GITEA_KEY_ID" // public key ID
2626
EnvDeployKeyID = "GITEA_DEPLOY_KEY_ID"
2727
EnvPRID = "GITEA_PR_ID"
28+
EnvPushTrigger = "GITEA_PUSH_TRIGGER"
2829
EnvIsInternal = "GITEA_INTERNAL_PUSH"
2930
EnvAppURL = "GITEA_ROOT_URL"
3031
EnvActionPerm = "GITEA_ACTION_PERM"
3132
)
3233

34+
type PushTrigger string
35+
36+
const (
37+
PushTriggerPRMergeToBase PushTrigger = "pr-merge-to-base"
38+
PushTriggerPRUpdateWithBase PushTrigger = "pr-update-with-base"
39+
)
40+
3341
// InternalPushingEnvironment returns an os environment to switch off hooks on push
3442
// It is recommended to avoid using this unless you are pushing within a transaction
3543
// or if you absolutely are sure that post-receive and pre-receive will do nothing

0 commit comments

Comments
 (0)