diff --git a/models/issues/issue_xref.go b/models/issues/issue_xref.go index e2e35859df..f8495929cf 100644 --- a/models/issues/issue_xref.go +++ b/models/issues/issue_xref.go @@ -235,7 +235,7 @@ func (issue *Issue) verifyReferencedIssue(stdCtx context.Context, ctx *crossRefe // AddCrossReferences add cross references func (c *Comment) AddCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error { - if c.Type != CommentTypeCode && c.Type != CommentTypeComment { + if !c.Type.HasContentSupport() { return nil } if err := c.LoadIssue(stdCtx); err != nil { diff --git a/models/issues/pull.go b/models/issues/pull.go index 0ff32e2473..2ff471b641 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -414,7 +414,7 @@ func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) } // GetGitRefName returns git ref for hidden pull request branch -func (pr *PullRequest) GetGitRefName() string { +func (pr *PullRequest) GetGitHeadRefName() string { return fmt.Sprintf("%s%d/head", git.PullPrefix, pr.Index) } diff --git a/models/user/setting_keys.go b/models/user/setting_options.go similarity index 76% rename from models/user/setting_keys.go rename to models/user/setting_options.go index 2c2ed078be..7be5039329 100644 --- a/models/user/setting_keys.go +++ b/models/user/setting_options.go @@ -21,4 +21,9 @@ const ( SignupUserAgent = "signup.user_agent" SettingsKeyCodeViewShowFileTree = "code_view.show_file_tree" + + SettingsKeyEmailNotificationGiteaActions = "email_notification.gitea_actions" + SettingEmailNotificationGiteaActionsAll = "all" + SettingEmailNotificationGiteaActionsFailureOnly = "failure-only" // Default for actions email preference + SettingEmailNotificationGiteaActionsDisabled = "disabled" ) diff --git a/modules/actions/artifacts.go b/modules/actions/artifacts.go index 4d074435ef..d28726e899 100644 --- a/modules/actions/artifacts.go +++ b/modules/actions/artifacts.go @@ -20,7 +20,7 @@ func IsArtifactV4(art *actions_model.ActionArtifact) bool { func DownloadArtifactV4ServeDirectOnly(ctx *context.Base, art *actions_model.ActionArtifact) (bool, error) { if setting.Actions.ArtifactStorage.ServeDirect() { - u, err := storage.ActionsArtifacts.URL(art.StoragePath, art.ArtifactPath, nil) + u, err := storage.ActionsArtifacts.URL(art.StoragePath, art.ArtifactPath, ctx.Req.Method, nil) if u != nil && err == nil { ctx.Redirect(u.String(), http.StatusFound) return true, nil diff --git a/modules/git/commit_info.go b/modules/git/commit_info.go index a796f4a204..9079e36677 100644 --- a/modules/git/commit_info.go +++ b/modules/git/commit_info.go @@ -15,7 +15,7 @@ type CommitInfo struct { func getCommitInfoSubmoduleFile(repoLink string, entry *TreeEntry, commit *Commit, treePathDir string) (*CommitSubmoduleFile, error) { fullPath := path.Join(treePathDir, entry.Name()) submodule, err := commit.GetSubModule(fullPath) - if err != nil { + if submodule == nil || err != nil { return nil, err } return NewCommitSubmoduleFile(repoLink, fullPath, submodule.URL, entry.ID.String()), nil diff --git a/modules/git/commit_info_test.go b/modules/git/commit_info_test.go index caaa6a502b..f8060bc33d 100644 --- a/modules/git/commit_info_test.go +++ b/modules/git/commit_info_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -120,6 +121,16 @@ func TestEntries_GetCommitsInfo(t *testing.T) { defer clonedRepo1.Close() testGetCommitsInfo(t, clonedRepo1) + + t.Run("NonExistingSubmoduleAsNil", func(t *testing.T) { + commit, err := bareRepo1.GetCommit("HEAD") + require.NoError(t, err) + tree, err := commit.GetTreeEntryByPath("file1.txt") + require.NoError(t, err) + cisf, err := getCommitInfoSubmoduleFile("/any/repo-link", tree, commit, "") + require.NoError(t, err) + assert.Nil(t, cisf) + }) } func BenchmarkEntries_GetCommitsInfo(b *testing.B) { diff --git a/modules/git/commit_submodule.go b/modules/git/commit_submodule.go index 031fd4e5d0..ff253b7eca 100644 --- a/modules/git/commit_submodule.go +++ b/modules/git/commit_submodule.go @@ -35,7 +35,8 @@ func (c *Commit) GetSubModules() (*ObjectCache[*SubModule], error) { return c.submoduleCache, nil } -// GetSubModule get the submodule according entry name +// GetSubModule gets the submodule by the entry name. +// It returns "nil, nil" if the submodule does not exist, caller should always remember to check the "nil" func (c *Commit) GetSubModule(entryName string) (*SubModule, error) { modules, err := c.GetSubModules() if err != nil { diff --git a/modules/migration/pullrequest.go b/modules/migration/pullrequest.go index fbfdff0315..c832879630 100644 --- a/modules/migration/pullrequest.go +++ b/modules/migration/pullrequest.go @@ -50,7 +50,7 @@ func (p *PullRequest) IsForkPullRequest() bool { } // GetGitRefName returns pull request relative path to head -func (p PullRequest) GetGitRefName() string { +func (p PullRequest) GetGitHeadRefName() string { return fmt.Sprintf("%s%d/head", git.PullPrefix, p.Number) } diff --git a/modules/packages/content_store.go b/modules/packages/content_store.go index dadb7eaefc..57974515e2 100644 --- a/modules/packages/content_store.go +++ b/modules/packages/content_store.go @@ -36,8 +36,8 @@ func (s *ContentStore) ShouldServeDirect() bool { return setting.Packages.Storage.ServeDirect() } -func (s *ContentStore) GetServeDirectURL(key BlobHash256Key, filename string, reqParams url.Values) (*url.URL, error) { - return s.store.URL(KeyToRelativePath(key), filename, reqParams) +func (s *ContentStore) GetServeDirectURL(key BlobHash256Key, filename, method string, reqParams url.Values) (*url.URL, error) { + return s.store.URL(KeyToRelativePath(key), filename, method, reqParams) } // FIXME: Workaround to be removed in v1.20 diff --git a/modules/storage/azureblob.go b/modules/storage/azureblob.go index 837afd0ba6..6860d81131 100644 --- a/modules/storage/azureblob.go +++ b/modules/storage/azureblob.go @@ -247,7 +247,7 @@ func (a *AzureBlobStorage) Delete(path string) error { } // URL gets the redirect URL to a file. The presigned link is valid for 5 minutes. -func (a *AzureBlobStorage) URL(path, name string, reqParams url.Values) (*url.URL, error) { +func (a *AzureBlobStorage) URL(path, name, _ string, reqParams url.Values) (*url.URL, error) { blobClient := a.getBlobClient(path) startTime := time.Now() diff --git a/modules/storage/helper.go b/modules/storage/helper.go index 9e6cceb537..f6c3d5eebb 100644 --- a/modules/storage/helper.go +++ b/modules/storage/helper.go @@ -30,7 +30,7 @@ func (s discardStorage) Delete(_ string) error { return fmt.Errorf("%s", s) } -func (s discardStorage) URL(_, _ string, _ url.Values) (*url.URL, error) { +func (s discardStorage) URL(_, _, _ string, _ url.Values) (*url.URL, error) { return nil, fmt.Errorf("%s", s) } diff --git a/modules/storage/helper_test.go b/modules/storage/helper_test.go index 62ebd8753c..3cba1e13c0 100644 --- a/modules/storage/helper_test.go +++ b/modules/storage/helper_test.go @@ -37,7 +37,7 @@ func Test_discardStorage(t *testing.T) { assert.Error(t, err, string(tt)) } { - got, err := tt.URL("path", "name", nil) + got, err := tt.URL("path", "name", "GET", nil) assert.Nil(t, got) assert.Errorf(t, err, string(tt)) } diff --git a/modules/storage/local.go b/modules/storage/local.go index 00c7f668aa..8a1776f606 100644 --- a/modules/storage/local.go +++ b/modules/storage/local.go @@ -114,7 +114,7 @@ func (l *LocalStorage) Delete(path string) error { } // URL gets the redirect URL to a file -func (l *LocalStorage) URL(path, name string, reqParams url.Values) (*url.URL, error) { +func (l *LocalStorage) URL(path, name, _ string, reqParams url.Values) (*url.URL, error) { return nil, ErrURLNotSupported } diff --git a/modules/storage/minio.go b/modules/storage/minio.go index 1c5d25b2d4..01f2c16267 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -279,7 +279,7 @@ func (m *MinioStorage) Delete(path string) error { } // URL gets the redirect URL to a file. The presigned link is valid for 5 minutes. -func (m *MinioStorage) URL(path, name string, serveDirectReqParams url.Values) (*url.URL, error) { +func (m *MinioStorage) URL(path, name, method string, serveDirectReqParams url.Values) (*url.URL, error) { // copy serveDirectReqParams reqParams, err := url.ParseQuery(serveDirectReqParams.Encode()) if err != nil { @@ -287,7 +287,12 @@ func (m *MinioStorage) URL(path, name string, serveDirectReqParams url.Values) ( } // TODO it may be good to embed images with 'inline' like ServeData does, but we don't want to have to read the file, do we? reqParams.Set("response-content-disposition", "attachment; filename=\""+quoteEscaper.Replace(name)+"\"") - u, err := m.client.PresignedGetObject(m.ctx, m.bucket, m.buildMinioPath(path), 5*time.Minute, reqParams) + expires := 5 * time.Minute + if method == http.MethodHead { + u, err := m.client.PresignedHeadObject(m.ctx, m.bucket, m.buildMinioPath(path), expires, reqParams) + return u, convertMinioErr(err) + } + u, err := m.client.PresignedGetObject(m.ctx, m.bucket, m.buildMinioPath(path), expires, reqParams) return u, convertMinioErr(err) } diff --git a/modules/storage/storage.go b/modules/storage/storage.go index b0529941e7..2313951d8a 100644 --- a/modules/storage/storage.go +++ b/modules/storage/storage.go @@ -63,7 +63,7 @@ type ObjectStorage interface { Save(path string, r io.Reader, size int64) (int64, error) Stat(path string) (os.FileInfo, error) Delete(path string) error - URL(path, name string, reqParams url.Values) (*url.URL, error) + URL(path, name, method string, reqParams url.Values) (*url.URL, error) IterateObjects(path string, iterator func(path string, obj Object) error) error } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index bedd55853a..37352aa554 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1021,6 +1021,8 @@ email_notifications.onmention = Only Email on Mention email_notifications.disable = Disable Email Notifications email_notifications.submit = Set Email Preference email_notifications.andyourown = And Your Own Notifications +email_notifications.actions.desc = Notifications for workflow runs on repositories set up with Gitea Actions. +email_notifications.actions.failure_only = Only notify for failed workflow runs visibility = User visibility visibility.public = Public diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 4870edf823..a47083b45e 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1840,7 +1840,7 @@ pulls.filter_branch=过滤分支 pulls.show_all_commits=显示所有提交 pulls.show_changes_since_your_last_review=显示自您上次审核以来的更改 pulls.showing_only_single_commit=仅显示提交 %[1]s 的更改 -pulls.showing_specified_commit_range=仅显示 %[1]s...%[2]s 之间的更改 +pulls.showing_specified_commit_range=仅显示 %[1]s..%[2]s 之间的更改 pulls.select_commit_hold_shift_for_range=选择提交。按住 Shift + 单击选择一个范围 pulls.review_only_possible_for_full_diff=只有在查看全部差异时才能进行审核 pulls.filter_changes_by_commit=按提交筛选 diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go index 6473659e5c..d71a6f487c 100644 --- a/routers/api/actions/artifacts.go +++ b/routers/api/actions/artifacts.go @@ -428,7 +428,7 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) { for _, artifact := range artifacts { var downloadURL string if setting.Actions.ArtifactStorage.ServeDirect() { - u, err := ar.fs.URL(artifact.StoragePath, artifact.ArtifactName, nil) + u, err := ar.fs.URL(artifact.StoragePath, artifact.ArtifactName, ctx.Req.Method, nil) if err != nil && !errors.Is(err, storage.ErrURLNotSupported) { log.Error("Error getting serve direct url: %v", err) } diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go index e9e9fc6393..6d27479628 100644 --- a/routers/api/actions/artifactsv4.go +++ b/routers/api/actions/artifactsv4.go @@ -517,7 +517,7 @@ func (r *artifactV4Routes) getSignedArtifactURL(ctx *ArtifactContext) { respData := GetSignedArtifactURLResponse{} if setting.Actions.ArtifactStorage.ServeDirect() { - u, err := storage.ActionsArtifacts.URL(artifact.StoragePath, artifact.ArtifactPath, nil) + u, err := storage.ActionsArtifacts.URL(artifact.StoragePath, artifact.ArtifactPath, ctx.Req.Method, nil) if u != nil && err == nil { respData.SignedUrl = u.String() } diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go index ba4a4f23ce..9265adcdba 100644 --- a/routers/api/packages/alpine/alpine.go +++ b/routers/api/packages/alpine/alpine.go @@ -75,6 +75,7 @@ func GetRepositoryFile(ctx *context.Context) { Filename: alpine_service.IndexArchiveFilename, CompositeKey: fmt.Sprintf("%s|%s|%s", ctx.PathParam("branch"), ctx.PathParam("repository"), ctx.PathParam("architecture")), }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, util.ErrNotExist) { @@ -216,7 +217,7 @@ func DownloadPackageFile(ctx *context.Context) { } } - s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0]) + s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0], ctx.Req.Method) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 878e0f9945..d914856706 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -339,7 +339,7 @@ func CommonRoutes() *web.Router { r.Group("/{packagename}/{packageversion}", func() { r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage) r.Group("/{filename}", func() { - r.Get("", generic.DownloadPackageFile) + r.Methods("HEAD,GET", "", generic.DownloadPackageFile) r.Group("", func() { r.Put("", generic.UploadPackage) r.Delete("", generic.DeletePackageFile) diff --git a/routers/api/packages/arch/arch.go b/routers/api/packages/arch/arch.go index bf9cc3f1b8..e5e25f443e 100644 --- a/routers/api/packages/arch/arch.go +++ b/routers/api/packages/arch/arch.go @@ -239,7 +239,7 @@ func GetPackageOrRepositoryFile(ctx *context.Context) { return } - s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0]) + s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0], ctx.Req.Method) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go index cfcf79244f..fc92089bb4 100644 --- a/routers/api/packages/cargo/cargo.go +++ b/routers/api/packages/cargo/cargo.go @@ -176,6 +176,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: strings.ToLower(fmt.Sprintf("%s-%s.crate", ctx.PathParam("package"), ctx.PathParam("version"))), }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go index 1f11afe548..be86e5d792 100644 --- a/routers/api/packages/chef/chef.go +++ b/routers/api/packages/chef/chef.go @@ -343,7 +343,7 @@ func DownloadPackage(ctx *context.Context) { pf := pd.Files[0].File - s, u, _, err := packages_service.OpenFileForDownload(ctx, pf) + s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go index 9daf0ffeff..d15fb689b3 100644 --- a/routers/api/packages/composer/composer.go +++ b/routers/api/packages/composer/composer.go @@ -171,6 +171,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: ctx.PathParam("filename"), }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go index fe70e02cd6..fb3aded9c4 100644 --- a/routers/api/packages/conan/conan.go +++ b/routers/api/packages/conan/conan.go @@ -492,6 +492,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe Filename: filename, CompositeKey: fileKey, }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go index e8c97503c8..3a523a01ab 100644 --- a/routers/api/packages/conda/conda.go +++ b/routers/api/packages/conda/conda.go @@ -317,7 +317,7 @@ func DownloadPackageFile(ctx *context.Context) { pf := pfs[0] - s, u, _, err := packages_service.OpenFileForDownload(ctx, pf) + s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index d532f698ad..b30522d68a 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -710,7 +710,7 @@ func DeleteManifest(ctx *context.Context) { func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor) { serveDirectReqParams := make(url.Values) serveDirectReqParams.Set("response-content-type", pfd.Properties.GetByName(container_module.PropertyMediaType)) - s, u, _, err := packages_service.OpenBlobForDownload(ctx, pfd.File, pfd.Blob, serveDirectReqParams) + s, u, _, err := packages_service.OpenBlobForDownload(ctx, pfd.File, pfd.Blob, ctx.Req.Method, serveDirectReqParams) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go index 732acd215f..46e1e0fcbb 100644 --- a/routers/api/packages/cran/cran.go +++ b/routers/api/packages/cran/cran.go @@ -250,7 +250,7 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) { return } - s, u, _, err := packages_service.OpenFileForDownload(ctx, pf) + s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go index 346f71fa5d..9bbcae3503 100644 --- a/routers/api/packages/debian/debian.go +++ b/routers/api/packages/debian/debian.go @@ -66,6 +66,7 @@ func GetRepositoryFile(ctx *context.Context) { Filename: ctx.PathParam("filename"), CompositeKey: key, }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { @@ -106,7 +107,7 @@ func GetRepositoryFileByHash(ctx *context.Context) { return } - s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0]) + s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0], ctx.Req.Method) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) @@ -222,6 +223,7 @@ func DownloadPackageFile(ctx *context.Context) { Filename: fmt.Sprintf("%s_%s_%s.deb", name, version, ctx.PathParam("architecture")), CompositeKey: fmt.Sprintf("%s|%s", ctx.PathParam("distribution"), ctx.PathParam("component")), }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, util.ErrNotExist) { diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go index db7aeace50..389f8ac1ca 100644 --- a/routers/api/packages/generic/generic.go +++ b/routers/api/packages/generic/generic.go @@ -42,6 +42,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: ctx.PathParam("filename"), }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go index 89ec86bce9..c2dd904254 100644 --- a/routers/api/packages/goproxy/goproxy.go +++ b/routers/api/packages/goproxy/goproxy.go @@ -106,7 +106,7 @@ func DownloadPackageFile(ctx *context.Context) { return } - s, u, _, err := packages_service.OpenFileForDownload(ctx, pfs[0]) + s, u, _, err := packages_service.OpenFileForDownload(ctx, pfs[0], ctx.Req.Method) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go index 39c34f4da4..b7f5d032fb 100644 --- a/routers/api/packages/helm/helm.go +++ b/routers/api/packages/helm/helm.go @@ -128,6 +128,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: filename, }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index 40a8ff8242..b6cfa893a4 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -223,7 +223,7 @@ func servePackageFile(ctx *context.Context, params parameters, serveContent bool return } - s, u, _, err := packages_service.OpenBlobForDownload(ctx, pf, pb, nil) + s, u, _, err := packages_service.OpenBlobForDownload(ctx, pf, pb, ctx.Req.Method, nil) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 1f09816d32..4f1099eb8b 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -96,6 +96,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: filename, }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { @@ -138,6 +139,7 @@ func DownloadPackageFileByName(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: filename, }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go index 92d62d90b1..023c1e36b3 100644 --- a/routers/api/packages/nuget/nuget.go +++ b/routers/api/packages/nuget/nuget.go @@ -416,6 +416,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: filename, }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { @@ -669,7 +670,7 @@ func DownloadSymbolFile(ctx *context.Context) { return } - s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0]) + s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0], ctx.Req.Method) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { apiError(ctx, http.StatusNotFound, err) diff --git a/routers/api/packages/pub/pub.go b/routers/api/packages/pub/pub.go index 4bd36e94b6..b246d1b34f 100644 --- a/routers/api/packages/pub/pub.go +++ b/routers/api/packages/pub/pub.go @@ -274,7 +274,7 @@ func DownloadPackageFile(ctx *context.Context) { pf := pd.Files[0].File - s, u, _, err := packages_service.OpenFileForDownload(ctx, pf) + s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go index 9b5ae6c89d..3a92387857 100644 --- a/routers/api/packages/pypi/pypi.go +++ b/routers/api/packages/pypi/pypi.go @@ -93,6 +93,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: filename, }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go index 938c35341d..e8e0f12fe3 100644 --- a/routers/api/packages/rpm/rpm.go +++ b/routers/api/packages/rpm/rpm.go @@ -103,6 +103,7 @@ func GetRepositoryFile(ctx *context.Context) { Filename: ctx.PathParam("filename"), CompositeKey: ctx.PathParam("group"), }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, util.ErrNotExist) { @@ -232,6 +233,7 @@ func DownloadPackageFile(ctx *context.Context) { Filename: fmt.Sprintf("%s-%s.%s.rpm", name, version, ctx.PathParam("architecture")), CompositeKey: ctx.PathParam("group"), }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, util.ErrNotExist) { diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go index 774d5520fd..7dc4b0c24d 100644 --- a/routers/api/packages/rubygems/rubygems.go +++ b/routers/api/packages/rubygems/rubygems.go @@ -184,6 +184,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: filename, }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index bf542f33a7..124aa98c3b 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -429,7 +429,7 @@ func DownloadPackageFile(ctx *context.Context) { pf := pd.Files[0].File - s, u, _, err := packages_service.OpenFileForDownload(ctx, pf) + s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return diff --git a/routers/api/packages/vagrant/vagrant.go b/routers/api/packages/vagrant/vagrant.go index 9eb67e5397..841fa8f964 100644 --- a/routers/api/packages/vagrant/vagrant.go +++ b/routers/api/packages/vagrant/vagrant.go @@ -229,6 +229,7 @@ func DownloadPackageFile(ctx *context.Context) { &packages_service.PackageFileInfo{ Filename: ctx.PathParam("provider"), }, + ctx.Req.Method, ) if err != nil { if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) { diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 69b5996222..a85dda79d0 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -210,7 +210,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) { if setting.LFS.Storage.ServeDirect() { // If we have a signed url (S3, object storage), redirect to this directly. - u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name(), nil) + u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name(), ctx.Req.Method, nil) if u != nil && err == nil { ctx.Redirect(u.String()) return @@ -331,7 +331,7 @@ func download(ctx *context.APIContext, archiveName string, archiver *repo_model. rPath := archiver.RelativePath() if setting.RepoArchive.Storage.ServeDirect() { // If we have a signed url (S3, object storage), redirect to this directly. - u, err := storage.RepoArchives.URL(rPath, downloadName, nil) + u, err := storage.RepoArchives.URL(rPath, downloadName, ctx.Req.Method, nil) if u != nil && err == nil { ctx.Redirect(u.String()) return diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 2c194f9253..e05b9b165c 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1461,9 +1461,9 @@ func GetPullRequestCommits(ctx *context.APIContext) { defer closer.Close() if pr.HasMerged { - prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName(), false, false) + prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitHeadRefName(), false, false) } else { - prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), false, false) + prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitHeadRefName(), false, false) } if err != nil { ctx.APIErrorInternal(err) @@ -1584,16 +1584,16 @@ func GetPullRequestFiles(ctx *context.APIContext) { var prInfo *git.CompareInfo if pr.HasMerged { - prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName(), true, false) + prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitHeadRefName(), true, false) } else { - prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), true, false) + prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitHeadRefName(), true, false) } if err != nil { ctx.APIErrorInternal(err) return } - headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 9421a052db..3c00193fac 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -336,7 +336,7 @@ func CreatePullReview(ctx *context.APIContext) { } defer closer.Close() - headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) + headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { ctx.APIErrorInternal(err) return @@ -455,7 +455,7 @@ func SubmitPullReview(ctx *context.APIContext) { return } - headCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(pr.GetGitRefName()) + headCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { ctx.APIErrorInternal(err) return diff --git a/routers/web/base.go b/routers/web/base.go index e43f36a97b..0f06cb5e4b 100644 --- a/routers/web/base.go +++ b/routers/web/base.go @@ -39,7 +39,7 @@ func avatarStorageHandler(storageSetting *setting.Storage, prefix string, objSto rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/") rPath = util.PathJoinRelX(rPath) - u, err := objStore.URL(rPath, path.Base(rPath), nil) + u, err := objStore.URL(rPath, path.Base(rPath), req.Method, nil) if err != nil { if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) { log.Warn("Unable to find %s %s", prefix, rPath) diff --git a/routers/web/devtest/mail_preview.go b/routers/web/devtest/mail_preview.go index 79dd441eab..d6bade15d7 100644 --- a/routers/web/devtest/mail_preview.go +++ b/routers/web/devtest/mail_preview.go @@ -16,7 +16,7 @@ import ( func MailPreviewRender(ctx *context.Context) { tmplName := ctx.PathParam("*") - mockDataContent, err := templates.AssetFS().ReadFile("mail/" + tmplName + ".mock.yml") + mockDataContent, err := templates.AssetFS().ReadFile("mail/" + tmplName + ".devtest.yml") mockData := map[string]any{} if err == nil { err = yaml.Unmarshal(mockDataContent, &mockData) diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go index 9eda926dad..f696669196 100644 --- a/routers/web/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -129,7 +129,7 @@ func ServeAttachment(ctx *context.Context, uuid string) { if setting.Attachment.Storage.ServeDirect() { // If we have a signed url (S3, object storage), redirect to this directly. - u, err := storage.Attachments.URL(attach.RelativePath(), attach.Name, nil) + u, err := storage.Attachments.URL(attach.RelativePath(), attach.Name, ctx.Req.Method, nil) if u != nil && err == nil { ctx.Redirect(u.String()) diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go index 020cebf196..6f394aae27 100644 --- a/routers/web/repo/download.go +++ b/routers/web/repo/download.go @@ -54,7 +54,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim if setting.LFS.Storage.ServeDirect() { // If we have a signed url (S3, object storage, blob storage), redirect to this directly. - u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name(), nil) + u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name(), ctx.Req.Method, nil) if u != nil && err == nil { ctx.Redirect(u.String()) return nil diff --git a/routers/web/repo/issue_comment.go b/routers/web/repo/issue_comment.go index c2a7f6b682..cb5b2d8019 100644 --- a/routers/web/repo/issue_comment.go +++ b/routers/web/repo/issue_comment.go @@ -103,7 +103,7 @@ func NewComment(ctx *context.Context) { // check whether the ref of PR in base repo is consistent with the head commit of head branch in the head repo // get head commit of PR if pull.Flow == issues_model.PullRequestFlowGithub { - prHeadRef := pull.GetGitRefName() + prHeadRef := pull.GetGitHeadRefName() if err := pull.LoadBaseRepo(ctx); err != nil { ctx.ServerError("Unable to load base repo", err) return diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go index d4458ed19e..d0064e763e 100644 --- a/routers/web/repo/issue_view.go +++ b/routers/web/repo/issue_view.go @@ -492,7 +492,7 @@ func preparePullViewSigning(ctx *context.Context, issue *issues_model.Issue) { pull := issue.PullRequest ctx.Data["WillSign"] = false if ctx.Doer != nil { - sign, key, _, err := asymkey_service.SignMerge(ctx, pull, ctx.Doer, pull.BaseRepo.RepoPath(), pull.BaseBranch, pull.GetGitRefName()) + sign, key, _, err := asymkey_service.SignMerge(ctx, pull, ctx.Doer, pull.BaseRepo.RepoPath(), pull.BaseBranch, pull.GetGitHeadRefName()) ctx.Data["WillSign"] = sign ctx.Data["SigningKey"] = key if err != nil { diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 23402e3eb2..c5302dd50f 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -196,7 +196,7 @@ func GetPullDiffStats(ctx *context.Context) { } // do not report 500 server error to end users if error occurs, otherwise a PR missing ref won't be able to view. - headCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(pull.GetGitRefName()) + headCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(pull.GetGitHeadRefName()) if err != nil { log.Error("Failed to GetRefCommitID: %v, repo: %v", err, ctx.Repo.Repository.FullName()) return @@ -218,13 +218,13 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri if pull.MergeBase == "" { var commitSHA, parentCommit string // If there is a head or a patch file, and it is readable, grab info - commitSHA, err := ctx.Repo.GitRepo.GetRefCommitID(pull.GetGitRefName()) + commitSHA, err := ctx.Repo.GitRepo.GetRefCommitID(pull.GetGitHeadRefName()) if err != nil { // Head File does not exist, try the patch commitSHA, err = ctx.Repo.GitRepo.ReadPatchCommit(pull.Index) if err == nil { // Recreate pull head in files for next time - if err := ctx.Repo.GitRepo.SetReference(pull.GetGitRefName(), commitSHA); err != nil { + if err := ctx.Repo.GitRepo.SetReference(pull.GetGitHeadRefName(), commitSHA); err != nil { log.Error("Could not write head file", err) } } else { @@ -274,7 +274,7 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) baseCommit := GetMergedBaseCommitID(ctx, issue) compareInfo, err := ctx.Repo.GitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(), - baseCommit, pull.GetGitRefName(), false, false) + baseCommit, pull.GetGitHeadRefName(), false, false) if err != nil { if strings.Contains(err.Error(), "fatal: Not a valid object name") || strings.Contains(err.Error(), "unknown revision or path not in the working tree") { ctx.Data["IsPullRequestBroken"] = true @@ -354,9 +354,9 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.Data["BaseTarget"] = pull.BaseBranch ctx.Data["HeadTarget"] = pull.HeadBranch - sha, err := baseGitRepo.GetRefCommitID(pull.GetGitRefName()) + sha, err := baseGitRepo.GetRefCommitID(pull.GetGitHeadRefName()) if err != nil { - ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err) + ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitHeadRefName()), err) return nil } commitStatuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll) @@ -374,7 +374,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C } compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), - pull.MergeBase, pull.GetGitRefName(), false, false) + pull.MergeBase, pull.GetGitHeadRefName(), false, false) if err != nil { if strings.Contains(err.Error(), "fatal: Not a valid object name") { ctx.Data["IsPullRequestBroken"] = true @@ -407,12 +407,12 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pull.Flow == issues_model.PullRequestFlowGithub { headBranchExist = gitrepo.IsBranchExist(ctx, pull.HeadRepo, pull.HeadBranch) } else { - headBranchExist = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitRefName()) + headBranchExist = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitHeadRefName()) } if headBranchExist { if pull.Flow != issues_model.PullRequestFlowGithub { - headBranchSha, err = baseGitRepo.GetRefCommitID(pull.GetGitRefName()) + headBranchSha, err = baseGitRepo.GetRefCommitID(pull.GetGitHeadRefName()) } else { headBranchSha, err = headGitRepo.GetBranchCommitID(pull.HeadBranch) } @@ -435,7 +435,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.Data["GetCommitMessages"] = "" } - sha, err := baseGitRepo.GetRefCommitID(pull.GetGitRefName()) + sha, err := baseGitRepo.GetRefCommitID(pull.GetGitHeadRefName()) if err != nil { if git.IsErrNotExist(err) { ctx.Data["IsPullRequestBroken"] = true @@ -451,7 +451,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C ctx.Data["NumFiles"] = 0 return nil } - ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err) + ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitHeadRefName()), err) return nil } @@ -522,7 +522,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C } compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), - git.BranchPrefix+pull.BaseBranch, pull.GetGitRefName(), false, false) + git.BranchPrefix+pull.BaseBranch, pull.GetGitHeadRefName(), false, false) if err != nil { if strings.Contains(err.Error(), "fatal: Not a valid object name") { ctx.Data["IsPullRequestBroken"] = true @@ -698,7 +698,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi return } - headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName()) + headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitHeadRefName()) if err != nil { ctx.ServerError("GetRefCommitID", err) return @@ -1509,7 +1509,7 @@ func CleanUpPullRequest(ctx *context.Context) { }() // Check if branch has no new commits - headCommitID, err := gitBaseRepo.GetRefCommitID(pr.GetGitRefName()) + headCommitID, err := gitBaseRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { log.Error("GetRefCommitID: %v", err) ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index 929e131d61..18e14e9b22 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -49,7 +49,7 @@ func RenderNewCodeCommentForm(ctx *context.Context) { ctx.Data["PageIsPullFiles"] = true ctx.Data["Issue"] = issue ctx.Data["CurrentReview"] = currentReview - pullHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(issue.PullRequest.GetGitRefName()) + pullHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(issue.PullRequest.GetGitHeadRefName()) if err != nil { ctx.ServerError("GetRefCommitID", err) return @@ -199,7 +199,7 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment, ori ctx.ServerError("comment.Issue.LoadPullRequest", err) return } - pullHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(comment.Issue.PullRequest.GetGitRefName()) + pullHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(comment.Issue.PullRequest.GetGitHeadRefName()) if err != nil { ctx.ServerError("GetRefCommitID", err) return diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 828ec08a8a..1b700aa6da 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -398,7 +398,7 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep rPath := archiver.RelativePath() if setting.RepoArchive.Storage.ServeDirect() { // If we have a signed url (S3, object storage), redirect to this directly. - u, err := storage.RepoArchives.URL(rPath, downloadName, nil) + u, err := storage.RepoArchives.URL(rPath, downloadName, ctx.Req.Method, nil) if u != nil && err == nil { ctx.Redirect(u.String()) return diff --git a/routers/web/user/package.go b/routers/web/user/package.go index 216acdf927..d130d1dca1 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -513,7 +513,7 @@ func DownloadPackageFile(ctx *context.Context) { return } - s, u, _, err := packages_service.OpenFileForDownload(ctx, pf) + s, u, _, err := packages_service.OpenFileForDownload(ctx, pf, ctx.Req.Method) if err != nil { ctx.ServerError("OpenFileForDownload", err) return diff --git a/routers/web/user/setting/notifications.go b/routers/web/user/setting/notifications.go index 16e58a0481..8ff6f1d941 100644 --- a/routers/web/user/setting/notifications.go +++ b/routers/web/user/setting/notifications.go @@ -4,11 +4,10 @@ package setting import ( - "errors" "net/http" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" @@ -29,6 +28,13 @@ func Notifications(ctx *context.Context) { ctx.Data["PageIsSettingsNotifications"] = true ctx.Data["EmailNotificationsPreference"] = ctx.Doer.EmailNotificationsPreference + actionsEmailPref, err := user_model.GetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyEmailNotificationGiteaActions, user_model.SettingEmailNotificationGiteaActionsFailureOnly) + if err != nil { + ctx.ServerError("GetUserSetting", err) + return + } + ctx.Data["ActionsEmailNotificationsPreference"] = actionsEmailPref + ctx.HTML(http.StatusOK, tplSettingsNotifications) } @@ -44,19 +50,40 @@ func NotificationsEmailPost(ctx *context.Context) { preference == user_model.EmailNotificationsOnMention || preference == user_model.EmailNotificationsDisabled || preference == user_model.EmailNotificationsAndYourOwn) { - log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name) - ctx.ServerError("SetEmailPreference", errors.New("option unrecognized")) + ctx.Flash.Error(ctx.Tr("invalid_data", preference)) + ctx.Redirect(setting.AppSubURL + "/user/settings/notifications") return } opts := &user.UpdateOptions{ EmailNotificationsPreference: optional.Some(preference), } if err := user.UpdateUser(ctx, ctx.Doer, opts); err != nil { - log.Error("Set Email Notifications failed: %v", err) ctx.ServerError("UpdateUser", err) return } - log.Trace("Email notifications preference made %s: %s", preference, ctx.Doer.Name) + ctx.Flash.Success(ctx.Tr("settings.email_preference_set_success")) + ctx.Redirect(setting.AppSubURL + "/user/settings/notifications") +} + +// NotificationsActionsEmailPost set user's email notification preference on Gitea Actions +func NotificationsActionsEmailPost(ctx *context.Context) { + if !setting.Actions.Enabled || unit.TypeActions.UnitGlobalDisabled() { + ctx.NotFound(nil) + return + } + + preference := ctx.FormString("preference") + if !(preference == user_model.SettingEmailNotificationGiteaActionsAll || + preference == user_model.SettingEmailNotificationGiteaActionsDisabled || + preference == user_model.SettingEmailNotificationGiteaActionsFailureOnly) { + ctx.Flash.Error(ctx.Tr("invalid_data", preference)) + ctx.Redirect(setting.AppSubURL + "/user/settings/notifications") + return + } + if err := user_model.SetUserSetting(ctx, ctx.Doer.ID, user_model.SettingsKeyEmailNotificationGiteaActions, preference); err != nil { + ctx.ServerError("SetUserSetting", err) + return + } ctx.Flash.Success(ctx.Tr("settings.email_preference_set_success")) ctx.Redirect(setting.AppSubURL + "/user/settings/notifications") } diff --git a/routers/web/web.go b/routers/web/web.go index 4d3d63ee27..7cb820cb59 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -598,6 +598,7 @@ func registerWebRoutes(m *web.Router) { m.Group("/notifications", func() { m.Get("", user_setting.Notifications) m.Post("/email", user_setting.NotificationsEmailPost) + m.Post("/actions", user_setting.NotificationsActionsEmailPost) }) m.Group("/security", func() { m.Get("", security.Security) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 8010f51a86..b8bc20cdbb 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -104,7 +104,7 @@ func (input *notifyInput) WithPayload(payload api.Payloader) *notifyInput { func (input *notifyInput) WithPullRequest(pr *issues_model.PullRequest) *notifyInput { input.PullRequest = pr if input.Ref == "" { - input.Ref = git.RefName(pr.GetGitRefName()) + input.Ref = git.RefName(pr.GetGitHeadRefName()) } return input } diff --git a/services/agit/agit.go b/services/agit/agit.go index 0ea8bbfa5d..63b3eab4f2 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -165,7 +165,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID) results = append(results, private.HookProcReceiveRefResult{ - Ref: pr.GetGitRefName(), + Ref: pr.GetGitHeadRefName(), OriginalRef: opts.RefFullNames[i], OldOID: objectFormat.EmptyObjectID().String(), NewOID: opts.NewCommitIDs[i], @@ -182,7 +182,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. return nil, fmt.Errorf("unable to load base repository for PR[%d] Error: %w", pr.ID, err) } - oldCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) + oldCommitID, err := gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { return nil, fmt.Errorf("unable to get ref commit id in base repository for PR[%d] Error: %w", pr.ID, err) } @@ -260,7 +260,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. results = append(results, private.HookProcReceiveRefResult{ OldOID: oldCommitID, NewOID: opts.NewCommitIDs[i], - Ref: pr.GetGitRefName(), + Ref: pr.GetGitHeadRefName(), OriginalRef: opts.RefFullNames[i], IsForcePush: isForcePush, IsCreatePR: false, diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go index 1a37d70e5a..a60883b4cc 100644 --- a/services/automerge/automerge.go +++ b/services/automerge/automerge.go @@ -187,7 +187,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) { } defer baseGitRepo.Close() - headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { log.Error("GetRefCommitID: %v", err) return @@ -225,7 +225,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) { return } case issues_model.PullRequestFlowAGit: - headBranchExist := gitrepo.IsReferenceExist(ctx, pr.BaseRepo, pr.GetGitRefName()) + headBranchExist := gitrepo.IsReferenceExist(ctx, pr.BaseRepo, pr.GetGitHeadRefName()) if !headBranchExist { log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch(Agit): %s]", pr, pr.HeadRepoID, pr.HeadBranch) return diff --git a/services/automergequeue/automergequeue.go b/services/automergequeue/automergequeue.go index cdf257e6c8..fa9c04da87 100644 --- a/services/automergequeue/automergequeue.go +++ b/services/automergequeue/automergequeue.go @@ -39,7 +39,7 @@ func StartPRCheckAndAutoMerge(ctx context.Context, pull *issues_model.PullReques return } defer gitRepo.Close() - commitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName()) + commitID, err := gitRepo.GetRefCommitID(pull.GetGitHeadRefName()) if err != nil { log.Error("GetRefCommitID: %v", err) return diff --git a/services/convert/pull.go b/services/convert/pull.go index 8f9679f649..4acbd880dc 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -112,7 +112,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u }, Head: &api.PRBranchInfo{ Name: pr.HeadBranch, - Ref: fmt.Sprintf("%s%d/head", git.PullPrefix, pr.Index), + Ref: pr.GetGitHeadRefName(), RepoID: -1, }, } @@ -170,9 +170,9 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u } if pr.Flow == issues_model.PullRequestFlowAGit { - apiPullRequest.Head.Sha, err = gitRepo.GetRefCommitID(pr.GetGitRefName()) + apiPullRequest.Head.Sha, err = gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { - log.Error("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err) + log.Error("GetRefCommitID[%s]: %v", pr.GetGitHeadRefName(), err) return nil } apiPullRequest.Head.RepoID = pr.BaseRepoID @@ -383,7 +383,7 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs }, Head: &api.PRBranchInfo{ Name: pr.HeadBranch, - Ref: pr.GetGitRefName(), + Ref: pr.GetGitHeadRefName(), RepoID: -1, }, } @@ -444,15 +444,15 @@ func ToAPIPullRequests(ctx context.Context, baseRepo *repo_model.Repository, prs } } if apiPullRequest.Head.Ref == "" { - apiPullRequest.Head.Ref = pr.GetGitRefName() + apiPullRequest.Head.Ref = pr.GetGitHeadRefName() } if pr.Flow == issues_model.PullRequestFlowAGit { apiPullRequest.Head.Name = "" } - apiPullRequest.Head.Sha, err = gitRepo.GetRefCommitID(pr.GetGitRefName()) + apiPullRequest.Head.Sha, err = gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { - log.Error("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err) + log.Error("GetRefCommitID[%s]: %v", pr.GetGitHeadRefName(), err) } if len(apiPullRequest.Head.Sha) == 0 && len(apiPullRequest.Head.Ref) != 0 { diff --git a/services/doctor/mergebase.go b/services/doctor/mergebase.go index 482bcd0a46..cbd8aa59fd 100644 --- a/services/doctor/mergebase.go +++ b/services/doctor/mergebase.go @@ -42,7 +42,7 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro if !pr.HasMerged { var err error - pr.MergeBase, _, err = git.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitRefName()).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + pr.MergeBase, _, err = git.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitHeadRefName()).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) if err != nil { var err2 error pr.MergeBase, _, err2 = git.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) @@ -63,7 +63,7 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro } refs := append([]string{}, parents[1:]...) - refs = append(refs, pr.GetGitRefName()) + refs = append(refs, pr.GetGitHeadRefName()) cmd := git.NewCommand("merge-base").AddDashesAndList(refs...) pr.MergeBase, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath}) if err != nil { diff --git a/services/issue/issue.go b/services/issue/issue.go index 2cb5f2801d..f03be3e18f 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -200,7 +200,7 @@ func DeleteIssue(ctx context.Context, doer *user_model.User, gitRepo *git.Reposi // delete pull request related git data if issue.IsPull && gitRepo != nil { - if err := gitRepo.RemoveReference(fmt.Sprintf("%s%d/head", git.PullPrefix, issue.PullRequest.Index)); err != nil { + if err := gitRepo.RemoveReference(issue.PullRequest.GetGitHeadRefName()); err != nil { return err } } diff --git a/services/issue/pull.go b/services/issue/pull.go index 3543b05b18..512fdf78e8 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -97,14 +97,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, pr *issues_model.PullReque } // get the mergebase - mergeBase, err := getMergeBase(repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName()) + mergeBase, err := getMergeBase(repo, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName()) if err != nil { return nil, err } // https://github.com/go-gitea/gitea/issues/29763, we need to get the files changed // between the merge base and the head commit but not the base branch and the head commit - changedFiles, err := repo.GetFilesChangedBetween(mergeBase, pr.GetGitRefName()) + changedFiles, err := repo.GetFilesChangedBetween(mergeBase, pr.GetGitHeadRefName()) if err != nil { return nil, err } diff --git a/services/lfs/server.go b/services/lfs/server.go index c44cc35e53..c9d9f164bf 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -43,6 +43,7 @@ type requestContext struct { User string Repo string Authorization string + Method string } // Claims is a JWT Token Claims @@ -397,6 +398,7 @@ func getRequestContext(ctx *context.Context) *requestContext { User: ctx.PathParam("username"), Repo: strings.TrimSuffix(ctx.PathParam("reponame"), ".git"), Authorization: ctx.Req.Header.Get("Authorization"), + Method: ctx.Req.Method, } } @@ -465,7 +467,7 @@ func buildObjectResponse(rc *requestContext, pointer lfs_module.Pointer, downloa var link *lfs_module.Link if setting.LFS.Storage.ServeDirect() { // If we have a signed url (S3, object storage), redirect to this directly. - u, err := storage.LFS.URL(pointer.RelativePath(), pointer.Oid, nil) + u, err := storage.LFS.URL(pointer.RelativePath(), pointer.Oid, rc.Method, nil) if u != nil && err == nil { // Presigned url does not need the Authorization header // https://github.com/go-gitea/gitea/issues/21525 diff --git a/services/mailer/mail.go b/services/mailer/mail.go index b7602e0321..d81b6d10af 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -174,3 +174,41 @@ func fromDisplayName(u *user_model.User) string { } return u.GetCompleteName() } + +func generateMetadataHeaders(repo *repo_model.Repository) map[string]string { + return map[string]string{ + // https://datatracker.ietf.org/doc/html/rfc2919 + "List-ID": fmt.Sprintf("%s <%s.%s.%s>", repo.FullName(), repo.Name, repo.OwnerName, setting.Domain), + + // https://datatracker.ietf.org/doc/html/rfc2369 + "List-Archive": fmt.Sprintf("<%s>", repo.HTMLURL()), + + "X-Mailer": "Gitea", + + "X-Gitea-Repository": repo.Name, + "X-Gitea-Repository-Path": repo.FullName(), + "X-Gitea-Repository-Link": repo.HTMLURL(), + + "X-GitLab-Project": repo.Name, + "X-GitLab-Project-Path": repo.FullName(), + } +} + +func generateSenderRecipientHeaders(doer, recipient *user_model.User) map[string]string { + return map[string]string{ + "X-Gitea-Sender": doer.Name, + "X-Gitea-Recipient": recipient.Name, + "X-Gitea-Recipient-Address": recipient.Email, + "X-GitHub-Sender": doer.Name, + "X-GitHub-Recipient": recipient.Name, + "X-GitHub-Recipient-Address": recipient.Email, + } +} + +func generateReasonHeaders(reason string) map[string]string { + return map[string]string{ + "X-Gitea-Reason": reason, + "X-GitHub-Reason": reason, + "X-GitLab-NotificationReason": reason, + } +} diff --git a/services/mailer/mail_issue_common.go b/services/mailer/mail_issue_common.go index 107f57772c..a34d8a68c9 100644 --- a/services/mailer/mail_issue_common.go +++ b/services/mailer/mail_issue_common.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "fmt" + "maps" "strconv" "strings" "time" @@ -29,7 +30,7 @@ import ( // Many e-mail service providers have limitations on the size of the email body, it's usually from 10MB to 25MB const maxEmailBodySize = 9_000_000 -func fallbackMailSubject(issue *issues_model.Issue) string { +func fallbackIssueMailSubject(issue *issues_model.Issue) string { return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index) } @@ -86,7 +87,7 @@ func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang if actName != "new" { prefix = "Re: " } - fallback = prefix + fallbackMailSubject(comment.Issue) + fallback = prefix + fallbackIssueMailSubject(comment.Issue) if comment.Comment != nil && comment.Comment.Review != nil { reviewComments = make([]*issues_model.Comment, 0, 10) @@ -202,7 +203,7 @@ func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang msg.SetHeader("References", references...) msg.SetHeader("List-Unsubscribe", listUnsubscribe...) - for key, value := range generateAdditionalHeaders(comment, actType, recipient) { + for key, value := range generateAdditionalHeadersForIssue(comment, actType, recipient) { msg.SetHeader(key, value) } @@ -302,35 +303,18 @@ func generateMessageIDForIssue(issue *issues_model.Issue, comment *issues_model. return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain) } -func generateAdditionalHeaders(ctx *mailComment, reason string, recipient *user_model.User) map[string]string { +func generateAdditionalHeadersForIssue(ctx *mailComment, reason string, recipient *user_model.User) map[string]string { repo := ctx.Issue.Repo - return map[string]string{ - // https://datatracker.ietf.org/doc/html/rfc2919 - "List-ID": fmt.Sprintf("%s <%s.%s.%s>", repo.FullName(), repo.Name, repo.OwnerName, setting.Domain), + issueID := strconv.FormatInt(ctx.Issue.Index, 10) + headers := generateMetadataHeaders(repo) - // https://datatracker.ietf.org/doc/html/rfc2369 - "List-Archive": fmt.Sprintf("<%s>", repo.HTMLURL()), + maps.Copy(headers, generateSenderRecipientHeaders(ctx.Doer, recipient)) + maps.Copy(headers, generateReasonHeaders(reason)) - "X-Mailer": "Gitea", - "X-Gitea-Reason": reason, - "X-Gitea-Sender": ctx.Doer.Name, - "X-Gitea-Recipient": recipient.Name, - "X-Gitea-Recipient-Address": recipient.Email, - "X-Gitea-Repository": repo.Name, - "X-Gitea-Repository-Path": repo.FullName(), - "X-Gitea-Repository-Link": repo.HTMLURL(), - "X-Gitea-Issue-ID": strconv.FormatInt(ctx.Issue.Index, 10), - "X-Gitea-Issue-Link": ctx.Issue.HTMLURL(), + headers["X-Gitea-Issue-ID"] = issueID + headers["X-Gitea-Issue-Link"] = ctx.Issue.HTMLURL() + headers["X-GitLab-Issue-IID"] = issueID - "X-GitHub-Reason": reason, - "X-GitHub-Sender": ctx.Doer.Name, - "X-GitHub-Recipient": recipient.Name, - "X-GitHub-Recipient-Address": recipient.Email, - - "X-GitLab-NotificationReason": reason, - "X-GitLab-Project": repo.Name, - "X-GitLab-Project-Path": repo.FullName(), - "X-GitLab-Issue-IID": strconv.FormatInt(ctx.Issue.Index, 10), - } + return headers } diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index 3996796beb..24f5d39d50 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -16,6 +16,7 @@ import ( "testing" texttmpl "text/template" + actions_model "code.gitea.io/gitea/models/actions" activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" @@ -298,13 +299,13 @@ func testComposeIssueCommentMessage(t *testing.T, ctx *mailComment, recipients [ return msgs[0] } -func TestGenerateAdditionalHeaders(t *testing.T) { +func TestGenerateAdditionalHeadersForIssue(t *testing.T) { doer, _, issue, _ := prepareMailerTest(t) comment := &mailComment{Issue: issue, Doer: doer} recipient := &user_model.User{Name: "test", Email: "test@gitea.com"} - headers := generateAdditionalHeaders(comment, "dummy-reason", recipient) + headers := generateAdditionalHeadersForIssue(comment, "dummy-reason", recipient) expected := map[string]string{ "List-ID": "user2/repo1 ", @@ -441,6 +442,16 @@ func TestGenerateMessageIDForRelease(t *testing.T) { assert.Equal(t, "", msgID) } +func TestGenerateMessageIDForActionsWorkflowRunStatusEmail(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: 795, RepoID: repo.ID}) + assert.NoError(t, run.LoadAttributes(db.DefaultContext)) + msgID := generateMessageIDForActionsWorkflowRunStatusEmail(repo, run) + assert.Equal(t, "", msgID) +} + func TestFromDisplayName(t *testing.T) { tmpl, err := texttmpl.New("mailFrom").Parse("{{ .DisplayName }}") assert.NoError(t, err) diff --git a/services/mailer/mail_workflow_run.go b/services/mailer/mail_workflow_run.go new file mode 100644 index 0000000000..29b3abda8e --- /dev/null +++ b/services/mailer/mail_workflow_run.go @@ -0,0 +1,165 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package mailer + +import ( + "bytes" + "context" + "fmt" + "sort" + + actions_model "code.gitea.io/gitea/models/actions" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/services/convert" + sender_service "code.gitea.io/gitea/services/mailer/sender" +) + +const tplWorkflowRun = "notify/workflow_run" + +type convertedWorkflowJob struct { + HTMLURL string + Status actions_model.Status + Name string + Attempt int64 +} + +func generateMessageIDForActionsWorkflowRunStatusEmail(repo *repo_model.Repository, run *actions_model.ActionRun) string { + return fmt.Sprintf("<%s/actions/runs/%d@%s>", repo.FullName(), run.Index, setting.Domain) +} + +func composeAndSendActionsWorkflowRunStatusEmail(ctx context.Context, repo *repo_model.Repository, run *actions_model.ActionRun, sender *user_model.User, recipients []*user_model.User) { + subject := "Run" + switch run.Status { + case actions_model.StatusFailure: + subject += " failed" + case actions_model.StatusCancelled: + subject += " cancelled" + case actions_model.StatusSuccess: + subject += " succeeded" + } + subject = fmt.Sprintf("%s: %s (%s)", subject, run.WorkflowID, base.ShortSha(run.CommitSHA)) + displayName := fromDisplayName(sender) + messageID := generateMessageIDForActionsWorkflowRunStatusEmail(repo, run) + metadataHeaders := generateMetadataHeaders(repo) + + jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID) + if err != nil { + log.Error("GetRunJobsByRunID: %v", err) + return + } + sort.SliceStable(jobs, func(i, j int) bool { + si, sj := jobs[i].Status, jobs[j].Status + /* + If both i and j are/are not success, leave it to si < sj. + If i is success and j is not, since the desired is j goes "smaller" and i goes "bigger", this func should return false. + If j is success and i is not, since the desired is i goes "smaller" and j goes "bigger", this func should return true. + */ + if si.IsSuccess() != sj.IsSuccess() { + return !si.IsSuccess() + } + return si < sj + }) + + convertedJobs := make([]convertedWorkflowJob, 0, len(jobs)) + for _, job := range jobs { + converted0, err := convert.ToActionWorkflowJob(ctx, repo, nil, job) + if err != nil { + log.Error("convert.ToActionWorkflowJob: %v", err) + continue + } + convertedJobs = append(convertedJobs, convertedWorkflowJob{ + HTMLURL: converted0.HTMLURL, + Name: converted0.Name, + Status: job.Status, + Attempt: converted0.RunAttempt, + }) + } + + langMap := make(map[string][]*user_model.User) + for _, user := range recipients { + langMap[user.Language] = append(langMap[user.Language], user) + } + for lang, tos := range langMap { + locale := translation.NewLocale(lang) + var runStatusText string + switch run.Status { + case actions_model.StatusSuccess: + runStatusText = "All jobs have succeeded" + case actions_model.StatusFailure: + runStatusText = "All jobs have failed" + for _, job := range jobs { + if !job.Status.IsFailure() { + runStatusText = "Some jobs were not successful" + break + } + } + case actions_model.StatusCancelled: + runStatusText = "All jobs have been cancelled" + } + var mailBody bytes.Buffer + if err := LoadedTemplates().BodyTemplates.ExecuteTemplate(&mailBody, tplWorkflowRun, map[string]any{ + "Subject": subject, + "Repo": repo, + "Run": run, + "RunStatusText": runStatusText, + "Jobs": convertedJobs, + "locale": locale, + }); err != nil { + log.Error("ExecuteTemplate [%s]: %v", tplWorkflowRun, err) + return + } + msgs := make([]*sender_service.Message, 0, len(tos)) + for _, rec := range tos { + msg := sender_service.NewMessageFrom( + rec.Email, + displayName, + setting.MailService.FromEmail, + subject, + mailBody.String(), + ) + msg.Info = subject + for k, v := range generateSenderRecipientHeaders(sender, rec) { + msg.SetHeader(k, v) + } + for k, v := range metadataHeaders { + msg.SetHeader(k, v) + } + msg.SetHeader("Message-ID", messageID) + msgs = append(msgs, msg) + } + SendAsync(msgs...) + } +} + +func MailActionsTrigger(ctx context.Context, sender *user_model.User, repo *repo_model.Repository, run *actions_model.ActionRun) { + if setting.MailService == nil { + return + } + if run.Status.IsSkipped() { + return + } + + recipients := make([]*user_model.User, 0) + + if !sender.IsGiteaActions() && !sender.IsGhost() && sender.IsMailable() { + notifyPref, err := user_model.GetUserSetting(ctx, sender.ID, + user_model.SettingsKeyEmailNotificationGiteaActions, user_model.SettingEmailNotificationGiteaActionsFailureOnly) + if err != nil { + log.Error("GetUserSetting: %v", err) + return + } + if notifyPref == user_model.SettingEmailNotificationGiteaActionsAll || !run.Status.IsSuccess() && notifyPref != user_model.SettingEmailNotificationGiteaActionsDisabled { + recipients = append(recipients, sender) + } + } + + if len(recipients) > 0 { + composeAndSendActionsWorkflowRunStatusEmail(ctx, repo, run, sender, recipients) + } +} diff --git a/services/mailer/notify.go b/services/mailer/notify.go index 77c366fe31..c008685e13 100644 --- a/services/mailer/notify.go +++ b/services/mailer/notify.go @@ -7,6 +7,7 @@ import ( "context" "fmt" + actions_model "code.gitea.io/gitea/models/actions" activities_model "code.gitea.io/gitea/models/activities" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" @@ -205,3 +206,10 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner * log.Error("SendRepoTransferNotifyMail: %v", err) } } + +func (m *mailNotifier) WorkflowRunStatusUpdate(ctx context.Context, repo *repo_model.Repository, sender *user_model.User, run *actions_model.ActionRun) { + if !run.Status.IsDone() { + return + } + MailActionsTrigger(ctx, sender, repo, run) +} diff --git a/services/migrations/dump.go b/services/migrations/dump.go index b4ca1e41e0..8edd567b08 100644 --- a/services/migrations/dump.go +++ b/services/migrations/dump.go @@ -488,7 +488,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR if pr.Head.CloneURL == "" || pr.Head.Ref == "" { // Set head information if pr.Head.SHA is available if pr.Head.SHA != "" { - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) + _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) if err != nil { log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err) } @@ -518,7 +518,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR if !ok { // Set head information if pr.Head.SHA is available if pr.Head.SHA != "" { - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) + _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) if err != nil { log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err) } @@ -577,7 +577,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR pr.Head.SHA = headSha } if pr.Head.SHA != "" { - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) + _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) if err != nil { log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) } diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 737bff24d0..75eb06d01f 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -681,7 +681,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *ba pr.Head.SHA = headSha } - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) + _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) if err != nil { return "", err } @@ -701,10 +701,10 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *ba _, _, err = git.NewCommand("rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) if err != nil { // Git update-ref remove bad references with a relative path - log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitRefName()) + log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitHeadRefName()) } else { // set head information - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) + _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) if err != nil { log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) } @@ -880,9 +880,9 @@ func (g *GiteaLocalUploader) CreateReviews(ctx context.Context, reviews ...*base continue } - headCommitID, err := g.gitRepo.GetRefCommitID(pr.GetGitRefName()) + headCommitID, err := g.gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { - log.Warn("PR #%d GetRefCommitID[%s] in %s/%s: %v, all review comments will be ignored", pr.Index, pr.GetGitRefName(), g.repoOwner, g.repoName, err) + log.Warn("PR #%d GetRefCommitID[%s] in %s/%s: %v, all review comments will be ignored", pr.Index, pr.GetGitHeadRefName(), g.repoOwner, g.repoName, err) continue } diff --git a/services/packages/packages.go b/services/packages/packages.go index 517334cbc7..4b16ee7285 100644 --- a/services/packages/packages.go +++ b/services/packages/packages.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "net/http" "net/url" "strings" @@ -564,7 +565,7 @@ func DeletePackageFile(ctx context.Context, pf *packages_model.PackageFile) erro } // OpenFileForDownloadByPackageNameAndVersion returns the content of the specific package file and increases the download counter. -func OpenFileForDownloadByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { +func OpenFileForDownloadByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo, method string) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { log.Trace("Getting package file stream: %v, %v, %s, %s, %s, %s", pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version, pfi.Filename, pfi.CompositeKey) pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version) @@ -576,27 +577,27 @@ func OpenFileForDownloadByPackageNameAndVersion(ctx context.Context, pvi *Packag return nil, nil, nil, err } - return OpenFileForDownloadByPackageVersion(ctx, pv, pfi) + return OpenFileForDownloadByPackageVersion(ctx, pv, pfi, method) } // OpenFileForDownloadByPackageVersion returns the content of the specific package file and increases the download counter. -func OpenFileForDownloadByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { +func OpenFileForDownloadByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo, method string) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey) if err != nil { return nil, nil, nil, err } - return OpenFileForDownload(ctx, pf) + return OpenFileForDownload(ctx, pf, method) } // OpenFileForDownload returns the content of the specific package file and increases the download counter. -func OpenFileForDownload(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { +func OpenFileForDownload(ctx context.Context, pf *packages_model.PackageFile, method string) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { pb, err := packages_model.GetBlobByID(ctx, pf.BlobID) if err != nil { return nil, nil, nil, err } - return OpenBlobForDownload(ctx, pf, pb, nil) + return OpenBlobForDownload(ctx, pf, pb, method, nil) } func OpenBlobStream(pb *packages_model.PackageBlob) (io.ReadSeekCloser, error) { @@ -607,7 +608,7 @@ func OpenBlobStream(pb *packages_model.PackageBlob) (io.ReadSeekCloser, error) { // OpenBlobForDownload returns the content of the specific package blob and increases the download counter. // If the storage supports direct serving and it's enabled, only the direct serving url is returned. -func OpenBlobForDownload(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob, serveDirectReqParams url.Values) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { +func OpenBlobForDownload(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob, method string, serveDirectReqParams url.Values) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { key := packages_module.BlobHash256Key(pb.HashSHA256) cs := packages_module.NewContentStore() @@ -617,23 +618,24 @@ func OpenBlobForDownload(ctx context.Context, pf *packages_model.PackageFile, pb var err error if cs.ShouldServeDirect() { - u, err = cs.GetServeDirectURL(key, pf.Name, serveDirectReqParams) + u, err = cs.GetServeDirectURL(key, pf.Name, method, serveDirectReqParams) if err != nil && !errors.Is(err, storage.ErrURLNotSupported) { - log.Error("Error getting serve direct url: %v", err) + log.Error("Error getting serve direct url (fallback to local reader): %v", err) } } if u == nil { s, err = cs.OpenBlob(key) } + if err != nil { + return nil, nil, nil, err + } - if err == nil { - if pf.IsLead { - if err := packages_model.IncrementDownloadCounter(ctx, pf.VersionID); err != nil { - log.Error("Error incrementing download counter: %v", err) - } + if pf.IsLead && method == http.MethodGet { + if err := packages_model.IncrementDownloadCounter(ctx, pf.VersionID); err != nil { + log.Error("Error incrementing download counter: %v", err) } } - return s, u, pf, err + return s, u, pf, nil } // RemoveAllPackages for User diff --git a/services/pull/check.go b/services/pull/check.go index bf6c5fa1c4..7fcec22f49 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -231,7 +231,7 @@ func isSignedIfRequired(ctx context.Context, pr *issues_model.PullRequest, doer return true, nil } - sign, _, _, err := asymkey_service.SignMerge(ctx, pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName()) + sign, _, _, err := asymkey_service.SignMerge(ctx, pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitHeadRefName()) return sign, err } @@ -277,7 +277,7 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com return nil, fmt.Errorf("unable to load base repo for %s: %w", pr, err) } - prHeadRef := pr.GetGitRefName() + prHeadRef := pr.GetGitHeadRefName() // Check if the pull request is merged into BaseBranch if _, _, err := git.NewCommand("merge-base", "--is-ancestor"). diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 7952ca6fe3..d15d318149 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -99,7 +99,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR if pr.Flow == issues_model.PullRequestFlowGithub && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) { return "", errors.New("Head branch does not exist, can not merge") } - if pr.Flow == issues_model.PullRequestFlowAGit && !gitrepo.IsReferenceExist(ctx, pr.HeadRepo, pr.GetGitRefName()) { + if pr.Flow == issues_model.PullRequestFlowAGit && !gitrepo.IsReferenceExist(ctx, pr.HeadRepo, pr.GetGitHeadRefName()) { return "", errors.New("Head branch does not exist, can not merge") } @@ -107,7 +107,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR if pr.Flow == issues_model.PullRequestFlowGithub { sha, err = headGitRepo.GetBranchCommitID(pr.HeadBranch) } else { - sha, err = headGitRepo.GetRefCommitID(pr.GetGitRefName()) + sha, err = headGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) } if err != nil { return "", err diff --git a/services/pull/patch.go b/services/pull/patch.go index 153e0baf87..9d9b8d0d07 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -41,7 +41,7 @@ func DownloadDiffOrPatch(ctx context.Context, pr *issues_model.PullRequest, w io } defer closer.Close() - compareArg := pr.MergeBase + "..." + pr.GetGitRefName() + compareArg := pr.MergeBase + "..." + pr.GetGitHeadRefName() switch { case patch: err = gitRepo.GetPatch(compareArg, w) diff --git a/services/pull/pull.go b/services/pull/pull.go index 701c4f4d32..2829e15441 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -143,7 +143,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { } compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), - git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false, false) + git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName(), false, false) if err != nil { return err } @@ -184,7 +184,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { return nil }); err != nil { // cleanup: this will only remove the reference, the real commit will be clean up when next GC - if err1 := baseGitRepo.RemoveReference(pr.GetGitRefName()); err1 != nil { + if err1 := baseGitRepo.RemoveReference(pr.GetGitHeadRefName()); err1 != nil { log.Error("RemoveReference: %v", err1) } return err @@ -567,7 +567,7 @@ func PushToBaseRepo(ctx context.Context, pr *issues_model.PullRequest) (err erro } func pushToBaseRepoHelper(ctx context.Context, pr *issues_model.PullRequest, prefixHeadBranch string) (err error) { - log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName()) + log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitHeadRefName()) if err := pr.LoadHeadRepo(ctx); err != nil { log.Error("Unable to load head repository for PR[%d] Error: %v", pr.ID, err) @@ -588,7 +588,7 @@ func pushToBaseRepoHelper(ctx context.Context, pr *issues_model.PullRequest, pre return fmt.Errorf("unable to load poster %d for pr %d: %w", pr.Issue.PosterID, pr.ID, err) } - gitRefName := pr.GetGitRefName() + gitRefName := pr.GetGitHeadRefName() if err := git.Push(ctx, headRepoPath, git.PushOptions{ Remote: baseRepoPath, @@ -642,13 +642,13 @@ func UpdatePullsRefs(ctx context.Context, repo *repo_model.Repository, update *r // UpdateRef update refs/pull/id/head directly for agit flow pull request func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) { - log.Trace("UpdateRef[%d]: upgate pull request ref in base repo '%s'", pr.ID, pr.GetGitRefName()) + log.Trace("UpdateRef[%d]: upgate pull request ref in base repo '%s'", pr.ID, pr.GetGitHeadRefName()) if err := pr.LoadBaseRepo(ctx); err != nil { log.Error("Unable to load base repository for PR[%d] Error: %v", pr.ID, err) return err } - _, _, err = git.NewCommand("update-ref").AddDynamicArguments(pr.GetGitRefName(), pr.HeadCommitID).RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()}) + _, _, err = git.NewCommand("update-ref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.HeadCommitID).RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()}) if err != nil { log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err) } @@ -816,9 +816,9 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ if pr.Flow == issues_model.PullRequestFlowGithub { headCommit, err = gitRepo.GetBranchCommit(pr.HeadBranch) } else { - pr.HeadCommitID, err = gitRepo.GetRefCommitID(pr.GetGitRefName()) + pr.HeadCommitID, err = gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { - log.Error("Unable to get head commit: %s Error: %v", pr.GetGitRefName(), err) + log.Error("Unable to get head commit: %s Error: %v", pr.GetGitHeadRefName(), err) return "" } headCommit, err = gitRepo.GetCommit(pr.HeadCommitID) @@ -993,7 +993,7 @@ func GetIssuesAllCommitStatus(ctx context.Context, issues issues_model.IssueList // getAllCommitStatus get pr's commit statuses. func getAllCommitStatus(ctx context.Context, gitRepo *git.Repository, pr *issues_model.PullRequest) (statuses []*git_model.CommitStatus, lastStatus *git_model.CommitStatus, err error) { - sha, shaErr := gitRepo.GetRefCommitID(pr.GetGitRefName()) + sha, shaErr := gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if shaErr != nil { return nil, nil, shaErr } @@ -1043,7 +1043,7 @@ func IsHeadEqualWithBranch(ctx context.Context, pr *issues_model.PullRequest, br return false, err } } else { - pr.HeadCommitID, err = baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + pr.HeadCommitID, err = baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { return false, err } @@ -1077,7 +1077,7 @@ func GetPullCommits(ctx *gitea_context.Context, issue *issues_model.Issue) ([]Co if pull.HasMerged { baseBranch = pull.MergeBase } - prInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), baseBranch, pull.GetGitRefName(), true, false) + prInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), baseBranch, pull.GetGitHeadRefName(), true, false) if err != nil { return nil, "", err } diff --git a/services/pull/review.go b/services/pull/review.go index 5c80e7b338..ee18db3859 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -200,7 +200,7 @@ func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_mo defer closer.Close() invalidated := false - head := pr.GetGitRefName() + head := pr.GetGitHeadRefName() if line > 0 { if reviewID != 0 { first, err := issues_model.FindComments(ctx, &issues_model.FindCommentsOptions{ @@ -237,16 +237,16 @@ func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_mo if err == nil { commitID = commit.ID.String() } else if !(strings.Contains(err.Error(), "exit status 128 - fatal: no such path") || notEnoughLines.MatchString(err.Error())) { - return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %w", pr.GetGitRefName(), gitRepo.Path, treePath, line, err) + return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %w", pr.GetGitHeadRefName(), gitRepo.Path, treePath, line, err) } } } // Only fetch diff if comment is review comment if len(patch) == 0 && reviewID != 0 { - headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) + headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { - return nil, fmt.Errorf("GetRefCommitID[%s]: %w", pr.GetGitRefName(), err) + return nil, fmt.Errorf("GetRefCommitID[%s]: %w", pr.GetGitHeadRefName(), err) } if len(commitID) == 0 { commitID = headCommitID @@ -301,7 +301,7 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos return nil, nil, ErrSubmitReviewOnClosedPR } - headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) + headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil { return nil, nil, err } diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go index 72406482e0..89f150fd92 100644 --- a/services/pull/temp_repo.go +++ b/services/pull/temp_repo.go @@ -174,7 +174,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) } else if len(pr.HeadCommitID) == objectFormat.FullLength() { // for not created pull request headBranch = pr.HeadCommitID } else { - headBranch = pr.GetGitRefName() + headBranch = pr.GetGitHeadRefName() } if err := git.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch). Run(ctx, prCtx.RunOpts()); err != nil { diff --git a/services/repository/branch.go b/services/repository/branch.go index dd00ca7dcd..6e0065b277 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -233,7 +233,7 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g defer baseGitRepo.Close() repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo } - pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) if err != nil && !git.IsErrNotExist(err) { return nil, fmt.Errorf("GetBranchCommitID: %v", err) } diff --git a/templates/mail/auth/activate.mock.yml b/templates/mail/auth/activate.devtest.yml similarity index 100% rename from templates/mail/auth/activate.mock.yml rename to templates/mail/auth/activate.devtest.yml diff --git a/templates/mail/notify/workflow_run.devtest.yml b/templates/mail/notify/workflow_run.devtest.yml new file mode 100644 index 0000000000..1e285be328 --- /dev/null +++ b/templates/mail/notify/workflow_run.devtest.yml @@ -0,0 +1,18 @@ +RunStatusText: run status text .... + +Repo: + FullName: RepoName + +Run: + WorkflowID: WorkflowID + HTMLURL: http://localhost/run/1 + +Jobs: + - Name: Job-Name-1 + Status: success + Attempt: 1 + HTMLURL: http://localhost/job/1 + - Name: Job-Name-2 + Status: failed + Attempt: 2 + HTMLURL: http://localhost/job/2 diff --git a/templates/mail/notify/workflow_run.tmpl b/templates/mail/notify/workflow_run.tmpl new file mode 100644 index 0000000000..f6dd8ad510 --- /dev/null +++ b/templates/mail/notify/workflow_run.tmpl @@ -0,0 +1,33 @@ + + + + + + {{.Subject}} + + + + + {{.Repo.FullName}} {{.Run.WorkflowID}}: {{.RunStatusText}} + + + + {{range $job := .Jobs}} + + + {{$job.Status}}: {{$job.Name}}{{if gt $job.Attempt 1}}, Attempt #{{$job.Attempt}}{{end}} + + + {{end}} + + + + + + + {{.locale.Tr "mail.view_it_on" AppName}} + + + + + diff --git a/templates/user/settings/notifications.tmpl b/templates/user/settings/notifications.tmpl index 4694bbb30a..40094aab4c 100644 --- a/templates/user/settings/notifications.tmpl +++ b/templates/user/settings/notifications.tmpl @@ -29,6 +29,37 @@ + + {{if .EnableActions}} + + {{ctx.Locale.Tr "actions.actions"}} + + + + + + {{$.CsrfTokenHtml}} + + {{ctx.Locale.Tr "settings.email_notifications.actions.desc" "https://docs.gitea.com/usage/actions/overview/"}} + + + {{svg "octicon-triangle-down" 14 "dropdown icon"}} + + + {{ctx.Locale.Tr "all"}} + {{ctx.Locale.Tr "settings.email_notifications.actions.failure_only"}} + {{ctx.Locale.Tr "disabled"}} + + + + + {{ctx.Locale.Tr "settings.email_notifications.submit"}} + + + + + + {{end}} {{template "user/settings/layout_footer" .}} diff --git a/tests/integration/api_packages_generic_test.go b/tests/integration/api_packages_generic_test.go index 5f410fc470..94e2c6072c 100644 --- a/tests/integration/api_packages_generic_test.go +++ b/tests/integration/api_packages_generic_test.go @@ -141,37 +141,25 @@ func TestPackageGeneric(t *testing.T) { t.Run("ServeDirect", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - if setting.Packages.Storage.Type != setting.MinioStorageType && setting.Packages.Storage.Type != setting.AzureBlobStorageType { - t.Skip("Test skipped for non-Minio-storage and non-AzureBlob-storage.") - return - } - if setting.Packages.Storage.Type == setting.MinioStorageType { - if !setting.Packages.Storage.MinioConfig.ServeDirect { - old := setting.Packages.Storage.MinioConfig.ServeDirect - defer func() { - setting.Packages.Storage.MinioConfig.ServeDirect = old - }() - - setting.Packages.Storage.MinioConfig.ServeDirect = true - } + defer test.MockVariableValue(&setting.Packages.Storage.MinioConfig.ServeDirect, true)() } else if setting.Packages.Storage.Type == setting.AzureBlobStorageType { - if !setting.Packages.Storage.AzureBlobConfig.ServeDirect { - old := setting.Packages.Storage.AzureBlobConfig.ServeDirect - defer func() { - setting.Packages.Storage.AzureBlobConfig.ServeDirect = old - }() - - setting.Packages.Storage.AzureBlobConfig.ServeDirect = true - } + defer test.MockVariableValue(&setting.Packages.Storage.AzureBlobConfig.ServeDirect, true)() + } else { + t.Skip("Test skipped for non-Minio-storage and non-AzureBlob-storage.") } - req := NewRequest(t, "GET", url+"/"+filename) - resp := MakeRequest(t, req, http.StatusSeeOther) + req = NewRequest(t, "HEAD", url+"/"+filename) + resp = MakeRequest(t, req, http.StatusSeeOther) + location := resp.Header().Get("Location") + assert.NotEmpty(t, location) + checkDownloadCount(2) + req = NewRequest(t, "GET", url+"/"+filename) + resp = MakeRequest(t, req, http.StatusSeeOther) checkDownloadCount(3) - location := resp.Header().Get("Location") + location = resp.Header().Get("Location") assert.NotEmpty(t, location) resp2, err := (&http.Client{}).Get(location) diff --git a/tests/integration/git_misc_test.go b/tests/integration/git_misc_test.go index a5c53fd6e9..d5a4af07ba 100644 --- a/tests/integration/git_misc_test.go +++ b/tests/integration/git_misc_test.go @@ -217,7 +217,7 @@ func TestAgitReviewStaleness(t *testing.T) { assert.NoError(t, err) defer baseGitRepo.Close() - updatedCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + updatedCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) assert.NoError(t, err) t.Logf("Updated commit ID: %s", updatedCommitID) diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 897a78cef4..3afa5f10f1 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -768,7 +768,7 @@ func TestPullAutoMergeAfterCommitStatusSucceed(t *testing.T) { // update commit status to success, then it should be merged automatically baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo) assert.NoError(t, err) - sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + sha, err := baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) assert.NoError(t, err) masterCommitID, err := baseGitRepo.GetBranchCommitID("master") assert.NoError(t, err) @@ -850,7 +850,7 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApproval(t *testing.T) { // update commit status to success, then it should be merged automatically baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo) assert.NoError(t, err) - sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + sha, err := baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) assert.NoError(t, err) masterCommitID, err := baseGitRepo.GetBranchCommitID("master") assert.NoError(t, err) @@ -979,7 +979,7 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApprovalForAgitFlow(t *testing. // update commit status to success, then it should be merged automatically baseGitRepo, err := gitrepo.OpenRepository(db.DefaultContext, baseRepo) assert.NoError(t, err) - sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) + sha, err := baseGitRepo.GetRefCommitID(pr.GetGitHeadRefName()) assert.NoError(t, err) masterCommitID, err := baseGitRepo.GetBranchCommitID("master") assert.NoError(t, err)