mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-22 17:58:03 +02:00
Merge remote-tracking branch 'upstream/main' into limit-repo-size
This commit is contained in:
@@ -75,7 +75,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
|
||||
}
|
||||
ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)
|
||||
state := toCommitStatus(job.Status)
|
||||
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{}); err == nil {
|
||||
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{ListAll: true}); err == nil {
|
||||
for _, v := range statuses {
|
||||
if v.Context == ctxname {
|
||||
if v.State == state {
|
||||
|
||||
@@ -143,14 +143,21 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||
}
|
||||
|
||||
var detectedWorkflows []*actions_module.DetectedWorkflow
|
||||
workflows, err := actions_module.DetectWorkflows(commit, input.Event, input.Payload)
|
||||
workflows, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||
}
|
||||
if len(workflows) == 0 {
|
||||
log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID)
|
||||
} else {
|
||||
actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig()
|
||||
|
||||
for _, wf := range workflows {
|
||||
if actionsConfig.IsWorkflowDisabled(wf.EntryName) {
|
||||
log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName)
|
||||
continue
|
||||
}
|
||||
|
||||
if wf.TriggerEvent != actions_module.GithubEventPullRequestTarget {
|
||||
detectedWorkflows = append(detectedWorkflows, wf)
|
||||
}
|
||||
@@ -164,7 +171,7 @@ func notify(ctx context.Context, input *notifyInput) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("gitRepo.GetCommit: %w", err)
|
||||
}
|
||||
baseWorkflows, err := actions_module.DetectWorkflows(baseCommit, input.Event, input.Payload)
|
||||
baseWorkflows, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("DetectWorkflows: %w", err)
|
||||
}
|
||||
|
||||
@@ -336,16 +336,7 @@ func InitSigningKey() error {
|
||||
// loadSymmetricKey checks if the configured secret is valid.
|
||||
// If it is not valid, it will return an error.
|
||||
func loadSymmetricKey() (any, error) {
|
||||
key := make([]byte, 32)
|
||||
n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != 32 {
|
||||
return nil, fmt.Errorf("JWT secret must be 32 bytes long")
|
||||
}
|
||||
|
||||
return key, nil
|
||||
return util.Base64FixedDecode(base64.RawURLEncoding, []byte(setting.OAuth2.JWTSecretBase64), 32)
|
||||
}
|
||||
|
||||
// loadOrCreateAsymmetricKey checks if the configured private key exists.
|
||||
|
||||
@@ -56,7 +56,7 @@ func (p *AuthSourceProvider) DisplayName() string {
|
||||
|
||||
func (p *AuthSourceProvider) IconHTML() template.HTML {
|
||||
if p.iconURL != "" {
|
||||
img := fmt.Sprintf(`<img class="gt-mr-3" width="20" height="20" src="%s" alt="%s">`,
|
||||
img := fmt.Sprintf(`<img class="gt-object-contain gt-mr-3" width="20" height="20" src="%s" alt="%s">`,
|
||||
html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()),
|
||||
)
|
||||
return template.HTML(img)
|
||||
|
||||
@@ -208,6 +208,7 @@ func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_m
|
||||
Exclusive: label.Exclusive,
|
||||
Color: strings.TrimLeft(label.Color, "#"),
|
||||
Description: label.Description,
|
||||
IsArchived: label.IsArchived(),
|
||||
}
|
||||
|
||||
// calculate URL
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package convert
|
||||
|
||||
import (
|
||||
secret_model "code.gitea.io/gitea/models/secret"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ToSecret converts Secret to API format
|
||||
func ToSecret(secret *secret_model.Secret) *api.Secret {
|
||||
result := &api.Secret{
|
||||
Name: secret.Name,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap
|
||||
ID: user.ID,
|
||||
UserName: user.Name,
|
||||
FullName: user.FullName,
|
||||
Email: user.GetEmail(),
|
||||
Email: user.GetPlaceholderEmail(),
|
||||
AvatarURL: user.AvatarLink(ctx),
|
||||
Created: user.CreatedUnix.AsTime(),
|
||||
Restricted: user.IsRestricted,
|
||||
|
||||
@@ -152,7 +152,7 @@ func registerCleanupPackages() {
|
||||
OlderThan: 24 * time.Hour,
|
||||
}, func(ctx context.Context, _ *user_model.User, config Config) error {
|
||||
realConfig := config.(*OlderThanConfig)
|
||||
return packages_cleanup_service.Cleanup(ctx, realConfig.OlderThan)
|
||||
return packages_cleanup_service.CleanupTask(ctx, realConfig.OlderThan)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"code.gitea.io/gitea/models/system"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/updatechecker"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
@@ -213,6 +214,16 @@ func registerGCLFS() {
|
||||
})
|
||||
}
|
||||
|
||||
func registerRebuildIssueIndexer() {
|
||||
RegisterTaskFatal("rebuild_issue_indexer", &BaseConfig{
|
||||
Enabled: false,
|
||||
RunAtStart: false,
|
||||
Schedule: "@annually",
|
||||
}, func(ctx context.Context, _ *user_model.User, config Config) error {
|
||||
return issue_indexer.PopulateIssueIndexer(ctx, false)
|
||||
})
|
||||
}
|
||||
|
||||
func initExtendedTasks() {
|
||||
registerDeleteInactiveUsers()
|
||||
registerDeleteRepositoryArchives()
|
||||
@@ -227,4 +238,5 @@ func initExtendedTasks() {
|
||||
registerUpdateGiteaChecker()
|
||||
registerDeleteOldSystemNotices()
|
||||
registerGCLFS()
|
||||
registerRebuildIssueIndexer()
|
||||
}
|
||||
|
||||
@@ -361,6 +361,7 @@ func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) b
|
||||
type NewTelegramHookForm struct {
|
||||
BotToken string `binding:"Required"`
|
||||
ChatID string `binding:"Required"`
|
||||
ThreadID string
|
||||
WebhookForm
|
||||
}
|
||||
|
||||
@@ -576,6 +577,7 @@ type CreateLabelForm struct {
|
||||
ID int64
|
||||
Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"`
|
||||
Exclusive bool `form:"exclusive"`
|
||||
IsArchived bool `form:"is_archived"`
|
||||
Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"`
|
||||
Color string `binding:"Required;MaxSize(7)" locale:"repo.issues.label_color"`
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"html/template"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -427,6 +426,23 @@ func (diffFile *DiffFile) ShouldBeHidden() bool {
|
||||
return diffFile.IsGenerated || diffFile.IsViewed
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) ModeTranslationKey(mode string) string {
|
||||
switch mode {
|
||||
case "040000":
|
||||
return "git.filemode.directory"
|
||||
case "100644":
|
||||
return "git.filemode.normal_file"
|
||||
case "100755":
|
||||
return "git.filemode.executable_file"
|
||||
case "120000":
|
||||
return "git.filemode.symbolic_link"
|
||||
case "160000":
|
||||
return "git.filemode.submodule"
|
||||
default:
|
||||
return mode
|
||||
}
|
||||
}
|
||||
|
||||
func getCommitFileLineCount(commit *git.Commit, filePath string) int {
|
||||
blob, err := commit.GetBlobByPath(filePath)
|
||||
if err != nil {
|
||||
@@ -1135,14 +1151,15 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
|
||||
}()
|
||||
|
||||
go func() {
|
||||
stderr := &bytes.Buffer{}
|
||||
cmdDiff.SetDescription(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath))
|
||||
if err := cmdDiff.Run(&git.RunOpts{
|
||||
Timeout: time.Duration(setting.Git.Timeout.Default) * time.Second,
|
||||
Dir: repoPath,
|
||||
Stderr: os.Stderr,
|
||||
Stdout: writer,
|
||||
Stderr: stderr,
|
||||
}); err != nil {
|
||||
log.Error("error during RunWithContext: %w", err)
|
||||
log.Error("error during GetDiff(git diff dir: %s): %v, stderr: %s", repoPath, err, stderr.String())
|
||||
}
|
||||
|
||||
_ = writer.Close()
|
||||
|
||||
@@ -33,7 +33,7 @@ func DeleteNotPassedAssignee(ctx context.Context, issue *issues_model.Issue, doe
|
||||
|
||||
if !found {
|
||||
// This function also does comments and hooks, which is why we call it separately instead of directly removing the assignees here
|
||||
if _, _, err := ToggleAssignee(ctx, issue, doer, assignee.ID); err != nil {
|
||||
if _, _, err := ToggleAssigneeWithNotify(ctx, issue, doer, assignee.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,8 @@ func DeleteNotPassedAssignee(ctx context.Context, issue *issues_model.Issue, doe
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToggleAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.
|
||||
func ToggleAssignee(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, assigneeID int64) (removed bool, comment *issues_model.Comment, err error) {
|
||||
// ToggleAssigneeWithNoNotify changes a user between assigned and not assigned for this issue, and make issue comment for it.
|
||||
func ToggleAssigneeWithNotify(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, assigneeID int64) (removed bool, comment *issues_model.Comment, err error) {
|
||||
removed, comment, err = issues_model.ToggleIssueAssignee(ctx, issue, doer, assigneeID)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
@@ -62,9 +62,9 @@ func ToggleAssignee(ctx context.Context, issue *issues_model.Issue, doer *user_m
|
||||
// ReviewRequest add or remove a review request from a user for this PR, and make comment for it.
|
||||
func ReviewRequest(ctx context.Context, issue *issues_model.Issue, doer, reviewer *user_model.User, isAdd bool) (comment *issues_model.Comment, err error) {
|
||||
if isAdd {
|
||||
comment, err = issues_model.AddReviewRequest(issue, reviewer, doer)
|
||||
comment, err = issues_model.AddReviewRequest(ctx, issue, reviewer, doer)
|
||||
} else {
|
||||
comment, err = issues_model.RemoveReviewRequest(issue, reviewer, doer)
|
||||
comment, err = issues_model.RemoveReviewRequest(ctx, issue, reviewer, doer)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -229,9 +229,9 @@ func IsValidTeamReviewRequest(ctx context.Context, reviewer *organization.Team,
|
||||
// TeamReviewRequest add or remove a review request from a team for this PR, and make comment for it.
|
||||
func TeamReviewRequest(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, reviewer *organization.Team, isAdd bool) (comment *issues_model.Comment, err error) {
|
||||
if isAdd {
|
||||
comment, err = issues_model.AddTeamReviewRequest(issue, reviewer, doer)
|
||||
comment, err = issues_model.AddTeamReviewRequest(ctx, issue, reviewer, doer)
|
||||
} else {
|
||||
comment, err = issues_model.RemoveTeamReviewRequest(issue, reviewer, doer)
|
||||
comment, err = issues_model.RemoveTeamReviewRequest(ctx, issue, reviewer, doer)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -15,26 +15,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
)
|
||||
|
||||
// CreateComment creates comment of issue or commit.
|
||||
func CreateComment(ctx context.Context, opts *issues_model.CreateCommentOptions) (comment *issues_model.Comment, err error) {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
comment, err = issues_model.CreateComment(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = committer.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return comment, nil
|
||||
}
|
||||
|
||||
// CreateRefComment creates a commit reference comment to issue.
|
||||
func CreateRefComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, commitSHA string) error {
|
||||
if len(commitSHA) == 0 {
|
||||
@@ -53,7 +33,7 @@ func CreateRefComment(ctx context.Context, doer *user_model.User, repo *repo_mod
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
_, err = issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
Type: issues_model.CommentTypeCommitRef,
|
||||
Doer: doer,
|
||||
Repo: repo,
|
||||
@@ -66,7 +46,7 @@ func CreateRefComment(ctx context.Context, doer *user_model.User, repo *repo_mod
|
||||
|
||||
// CreateIssueComment creates a plain issue comment.
|
||||
func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content string, attachments []string) (*issues_model.Comment, error) {
|
||||
comment, err := CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
comment, err := issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
Type: issues_model.CommentTypeComment,
|
||||
Doer: doer,
|
||||
Repo: repo,
|
||||
|
||||
+14
-14
@@ -27,7 +27,7 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_mo
|
||||
}
|
||||
|
||||
for _, assigneeID := range assigneeIDs {
|
||||
if err := AddAssigneeIfNotAssigned(ctx, issue, issue.Poster, assigneeID); err != nil {
|
||||
if _, err := AddAssigneeIfNotAssigned(ctx, issue, issue.Poster, assigneeID, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ func UpdateAssignees(ctx context.Context, issue *issues_model.Issue, oneAssignee
|
||||
// has access to the repo.
|
||||
for _, assignee := range allNewAssignees {
|
||||
// Extra method to prevent double adding (which would result in removing)
|
||||
err = AddAssigneeIfNotAssigned(ctx, issue, doer, assignee.ID)
|
||||
_, err = AddAssigneeIfNotAssigned(ctx, issue, doer, assignee.ID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -173,36 +173,36 @@ func DeleteIssue(ctx context.Context, doer *user_model.User, gitRepo *git.Reposi
|
||||
|
||||
// AddAssigneeIfNotAssigned adds an assignee only if he isn't already assigned to the issue.
|
||||
// Also checks for access of assigned user
|
||||
func AddAssigneeIfNotAssigned(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, assigneeID int64) (err error) {
|
||||
func AddAssigneeIfNotAssigned(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, assigneeID int64, notify bool) (comment *issues_model.Comment, err error) {
|
||||
assignee, err := user_model.GetUserByID(ctx, assigneeID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if the user is already assigned
|
||||
isAssigned, err := issues_model.IsUserAssignedToIssue(ctx, issue, assignee)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if isAssigned {
|
||||
// nothing to to
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
valid, err := access_model.CanBeAssigned(ctx, assignee, issue.Repo, issue.IsPull)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if !valid {
|
||||
return repo_model.ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: issue.Repo.Name}
|
||||
return nil, repo_model.ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: issue.Repo.Name}
|
||||
}
|
||||
|
||||
_, _, err = ToggleAssignee(ctx, issue, doer, assigneeID)
|
||||
if err != nil {
|
||||
return err
|
||||
if notify {
|
||||
_, comment, err = ToggleAssigneeWithNotify(ctx, issue, doer, assigneeID)
|
||||
return comment, err
|
||||
}
|
||||
|
||||
return nil
|
||||
_, comment, err = issues_model.ToggleIssueAssignee(ctx, issue, doer, assigneeID)
|
||||
return comment, err
|
||||
}
|
||||
|
||||
// GetRefEndNamesAndURLs retrieves the ref end names (e.g. refs/heads/branch-name -> branch-name)
|
||||
@@ -248,7 +248,7 @@ func deleteIssue(ctx context.Context, issue *issues_model.Issue) error {
|
||||
issue.MilestoneID, err)
|
||||
}
|
||||
|
||||
if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil {
|
||||
if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID, issue.Index); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,17 @@ import (
|
||||
debian_service "code.gitea.io/gitea/services/packages/debian"
|
||||
)
|
||||
|
||||
// Cleanup removes expired package data
|
||||
func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
|
||||
ctx, committer, err := db.TxContext(taskCtx)
|
||||
// Task method to execute cleanup rules and cleanup expired package data
|
||||
func CleanupTask(ctx context.Context, olderThan time.Duration) error {
|
||||
if err := ExecuteCleanupRules(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return CleanupExpiredData(ctx, olderThan)
|
||||
}
|
||||
|
||||
func ExecuteCleanupRules(outerCtx context.Context) error {
|
||||
ctx, committer, err := db.TxContext(outerCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -30,7 +38,7 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
|
||||
|
||||
err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error {
|
||||
select {
|
||||
case <-taskCtx.Done():
|
||||
case <-outerCtx.Done():
|
||||
return db.ErrCancelledf("While processing package cleanup rules")
|
||||
default:
|
||||
}
|
||||
@@ -122,6 +130,16 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error {
|
||||
ctx, committer, err := db.TxContext(outerCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
if err := container_service.Cleanup(ctx, olderThan); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ func buildPackagesIndices(ctx context.Context, ownerID int64, repoVersion *packa
|
||||
}
|
||||
addSeparator = true
|
||||
|
||||
fmt.Fprint(w, pfd.Properties.GetByName(debian_module.PropertyControl))
|
||||
fmt.Fprintf(w, "%s\n", strings.TrimSpace(pfd.Properties.GetByName(debian_module.PropertyControl)))
|
||||
|
||||
fmt.Fprintf(w, "Filename: pool/%s/%s/%s\n", distribution, component, pfd.File.Name)
|
||||
fmt.Fprintf(w, "Size: %d\n", pfd.Blob.Size)
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
)
|
||||
|
||||
// getCommitIDsFromRepo get commit IDs from repo in between oldCommitID and newCommitID
|
||||
@@ -90,7 +89,7 @@ func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *iss
|
||||
|
||||
ops.Content = string(dataJSON)
|
||||
|
||||
comment, err = issue_service.CreateComment(ctx, ops)
|
||||
comment, err = issues_model.CreateComment(ctx, ops)
|
||||
|
||||
return comment, err
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
|
||||
return "", errors.Wrap(err, "LoadBaseRepo")
|
||||
}
|
||||
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptions{})
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptions{ListAll: true})
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "GetLatestCommitStatus")
|
||||
}
|
||||
|
||||
@@ -62,14 +62,19 @@ func TestPatch(pr *issues_model.PullRequest) error {
|
||||
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("TestPatch: %s", pr))
|
||||
defer finished()
|
||||
|
||||
// Clone base repo.
|
||||
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
|
||||
if err != nil {
|
||||
log.Error("createTemporaryRepoForPR %-v: %v", pr, err)
|
||||
if !git_model.IsErrBranchNotExist(err) {
|
||||
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
return testPatch(ctx, prCtx, pr)
|
||||
}
|
||||
|
||||
func testPatch(ctx context.Context, prCtx *prContext, pr *issues_model.PullRequest) error {
|
||||
gitRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %w", err)
|
||||
|
||||
+154
-56
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@@ -17,13 +18,14 @@ import (
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
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/container"
|
||||
gitea_context "code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
@@ -35,73 +37,70 @@ import (
|
||||
var pullWorkingPool = sync.NewExclusivePool()
|
||||
|
||||
// NewPullRequest creates new pull request with labels for repository.
|
||||
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, pull *issues_model.Issue, labelIDs []int64, uuids []string, pr *issues_model.PullRequest, assigneeIDs []int64) error {
|
||||
if err := TestPatch(pr); err != nil {
|
||||
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *issues_model.Issue, labelIDs []int64, uuids []string, pr *issues_model.PullRequest, assigneeIDs []int64) error {
|
||||
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
|
||||
if err != nil {
|
||||
if !git_model.IsErrBranchNotExist(err) {
|
||||
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
if err := testPatch(ctx, prCtx, pr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
divergence, err := GetDiverging(ctx, pr)
|
||||
divergence, err := git.GetDivergingCommits(ctx, prCtx.tmpBasePath, baseBranch, trackingBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pr.CommitsAhead = divergence.Ahead
|
||||
pr.CommitsBehind = divergence.Behind
|
||||
|
||||
if err := issues_model.NewPullRequest(ctx, repo, pull, labelIDs, uuids, pr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, assigneeID := range assigneeIDs {
|
||||
if err := issue_service.AddAssigneeIfNotAssigned(ctx, pull, pull.Poster, assigneeID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pr.Issue = pull
|
||||
pull.PullRequest = pr
|
||||
|
||||
// Now - even if the request context has been cancelled as the PR has been created
|
||||
// in the db and there is no way to cancel that transaction we have to proceed - therefore
|
||||
// create new context and work from there
|
||||
prCtx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("NewPullRequest: %s:%d", repo.FullName(), pr.Index))
|
||||
defer finished()
|
||||
|
||||
if pr.Flow == issues_model.PullRequestFlowGithub {
|
||||
err = PushToBaseRepo(prCtx, pr)
|
||||
} else {
|
||||
err = UpdateRef(prCtx, pr)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, pull, pull.Poster, pull.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notification.NotifyNewPullRequest(prCtx, pr, mentions)
|
||||
if len(pull.Labels) > 0 {
|
||||
notification.NotifyIssueChangeLabels(prCtx, pull.Poster, pull, pull.Labels, nil)
|
||||
}
|
||||
if pull.Milestone != nil {
|
||||
notification.NotifyIssueChangeMilestone(prCtx, pull.Poster, pull, 0)
|
||||
}
|
||||
assigneeCommentMap := make(map[int64]*issues_model.Comment)
|
||||
|
||||
// add first push codes comment
|
||||
baseGitRepo, err := git.OpenRepository(prCtx, pr.BaseRepo.RepoPath())
|
||||
baseGitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer baseGitRepo.Close()
|
||||
|
||||
compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(),
|
||||
git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if err := issues_model.NewPullRequest(ctx, repo, issue, labelIDs, uuids, pr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, assigneeID := range assigneeIDs {
|
||||
comment, err := issue_service.AddAssigneeIfNotAssigned(ctx, issue, issue.Poster, assigneeID, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
assigneeCommentMap[assigneeID] = comment
|
||||
}
|
||||
|
||||
pr.Issue = issue
|
||||
issue.PullRequest = pr
|
||||
|
||||
if pr.Flow == issues_model.PullRequestFlowGithub {
|
||||
err = PushToBaseRepo(ctx, pr)
|
||||
} else {
|
||||
err = UpdateRef(ctx, pr)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(),
|
||||
git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(compareInfo.Commits) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(compareInfo.Commits) > 0 {
|
||||
data := issues_model.PushActionContent{IsForcePush: false}
|
||||
data.CommitIDs = make([]string, 0, len(compareInfo.Commits))
|
||||
for i := len(compareInfo.Commits) - 1; i >= 0; i-- {
|
||||
@@ -115,21 +114,52 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, pull *issu
|
||||
|
||||
ops := &issues_model.CreateCommentOptions{
|
||||
Type: issues_model.CommentTypePullRequestPush,
|
||||
Doer: pull.Poster,
|
||||
Doer: issue.Poster,
|
||||
Repo: repo,
|
||||
Issue: pr.Issue,
|
||||
IsForcePush: false,
|
||||
Content: string(dataJSON),
|
||||
}
|
||||
|
||||
_, _ = issue_service.CreateComment(ctx, ops)
|
||||
if _, err = issues_model.CreateComment(ctx, ops); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !pr.IsWorkInProgress() {
|
||||
if err := issues_model.PullRequestCodeOwnersReview(ctx, pull, pr); err != nil {
|
||||
if err := issues_model.PullRequestCodeOwnersReview(ctx, issue, pr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
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 {
|
||||
log.Error("RemoveReference: %v", err1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
baseGitRepo.Close() // close immediately to avoid notifications will open the repository again
|
||||
|
||||
mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notification.NotifyNewPullRequest(ctx, pr, mentions)
|
||||
if len(issue.Labels) > 0 {
|
||||
notification.NotifyIssueChangeLabels(ctx, issue.Poster, issue, issue.Labels, nil)
|
||||
}
|
||||
if issue.Milestone != nil {
|
||||
notification.NotifyIssueChangeMilestone(ctx, issue.Poster, issue, 0)
|
||||
}
|
||||
if len(assigneeIDs) > 0 {
|
||||
for _, assigneeID := range assigneeIDs {
|
||||
assignee, err := user_model.GetUserByID(ctx, assigneeID)
|
||||
if err != nil {
|
||||
return ErrDependenciesLeft
|
||||
}
|
||||
notification.NotifyIssueChangeAssignee(ctx, issue.Poster, issue, assignee, false, assigneeCommentMap[assigneeID])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -228,7 +258,7 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
|
||||
OldRef: oldBranch,
|
||||
NewRef: targetBranch,
|
||||
}
|
||||
if _, err = issue_service.CreateComment(ctx, options); err != nil {
|
||||
if _, err = issues_model.CreateComment(ctx, options); err != nil {
|
||||
return fmt.Errorf("CreateChangeTargetBranchComment: %w", err)
|
||||
}
|
||||
|
||||
@@ -801,7 +831,7 @@ func getAllCommitStatus(gitRepo *git.Repository, pr *issues_model.PullRequest) (
|
||||
return nil, nil, shaErr
|
||||
}
|
||||
|
||||
statuses, _, err = git_model.GetLatestCommitStatus(db.DefaultContext, pr.BaseRepo.ID, sha, db.ListOptions{})
|
||||
statuses, _, err = git_model.GetLatestCommitStatus(db.DefaultContext, pr.BaseRepo.ID, sha, db.ListOptions{ListAll: true})
|
||||
lastStatus = git_model.CalcCommitStatus(statuses)
|
||||
return statuses, lastStatus, err
|
||||
}
|
||||
@@ -856,3 +886,71 @@ func IsHeadEqualWithBranch(ctx context.Context, pr *issues_model.PullRequest, br
|
||||
}
|
||||
return baseCommit.HasPreviousCommit(headCommit.ID)
|
||||
}
|
||||
|
||||
type CommitInfo struct {
|
||||
Summary string `json:"summary"`
|
||||
CommitterOrAuthorName string `json:"committer_or_author_name"`
|
||||
ID string `json:"id"`
|
||||
ShortSha string `json:"short_sha"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
|
||||
// GetPullCommits returns all commits on given pull request and the last review commit sha
|
||||
func GetPullCommits(ctx *gitea_context.Context, issue *issues_model.Issue) ([]CommitInfo, string, error) {
|
||||
pull := issue.PullRequest
|
||||
|
||||
baseGitRepo := ctx.Repo.GitRepo
|
||||
|
||||
if err := pull.LoadBaseRepo(ctx); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
baseBranch := pull.BaseBranch
|
||||
if pull.HasMerged {
|
||||
baseBranch = pull.MergeBase
|
||||
}
|
||||
prInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(), baseBranch, pull.GetGitRefName(), true, false)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
commits := make([]CommitInfo, 0, len(prInfo.Commits))
|
||||
|
||||
for _, commit := range prInfo.Commits {
|
||||
var committerOrAuthorName string
|
||||
var commitTime time.Time
|
||||
if commit.Committer != nil {
|
||||
committerOrAuthorName = commit.Committer.Name
|
||||
commitTime = commit.Committer.When
|
||||
} else {
|
||||
committerOrAuthorName = commit.Author.Name
|
||||
commitTime = commit.Author.When
|
||||
}
|
||||
|
||||
commits = append(commits, CommitInfo{
|
||||
Summary: commit.Summary(),
|
||||
CommitterOrAuthorName: committerOrAuthorName,
|
||||
ID: commit.ID.String(),
|
||||
ShortSha: base.ShortSha(commit.ID.String()),
|
||||
Time: commitTime.Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
|
||||
var lastReviewCommitID string
|
||||
if ctx.IsSigned {
|
||||
// get last review of current user and store information in context (if available)
|
||||
lastreview, err := issues_model.FindLatestReviews(ctx, issues_model.FindReviewOptions{
|
||||
IssueID: issue.ID,
|
||||
ReviewerID: ctx.Doer.ID,
|
||||
Type: issues_model.ReviewTypeUnknown,
|
||||
})
|
||||
|
||||
if err != nil && !issues_model.IsErrReviewNotExist(err) {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(lastreview) > 0 {
|
||||
lastReviewCommitID = lastreview[0].CommitID
|
||||
}
|
||||
}
|
||||
|
||||
return commits, lastReviewCommitID, nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
)
|
||||
|
||||
var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`)
|
||||
@@ -248,7 +247,7 @@ func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_mo
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return issue_service.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
return issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
Type: issues_model.CommentTypeCode,
|
||||
Doer: doer,
|
||||
Repo: repo,
|
||||
@@ -340,7 +339,7 @@ func DismissApprovalReviews(ctx context.Context, doer *user_model.User, pull *is
|
||||
return err
|
||||
}
|
||||
|
||||
comment, err := issue_service.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
comment, err := issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
Doer: doer,
|
||||
Content: "New commits pushed, approval review dismissed automatically according to repository settings",
|
||||
Type: issues_model.CommentTypeDismissReview,
|
||||
@@ -411,7 +410,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comment, err = issue_service.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
comment, err = issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
|
||||
Doer: doer,
|
||||
Content: message,
|
||||
Type: issues_model.CommentTypeDismissReview,
|
||||
|
||||
@@ -263,7 +263,9 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
|
||||
}
|
||||
|
||||
if repo.IsEmpty {
|
||||
_ = repo_model.UpdateRepositoryCols(ctx, &repo_model.Repository{ID: repo.ID, IsEmpty: false, DefaultBranch: opts.NewBranch}, "is_empty", "default_branch")
|
||||
if isEmpty, err := gitRepo.IsEmpty(); err == nil && !isEmpty {
|
||||
_ = repo_model.UpdateRepositoryCols(ctx, &repo_model.Repository{ID: repo.ID, IsEmpty: false, DefaultBranch: opts.NewBranch}, "is_empty", "default_branch")
|
||||
}
|
||||
}
|
||||
|
||||
return filesResponse, nil
|
||||
|
||||
@@ -28,8 +28,9 @@ import (
|
||||
|
||||
// WebSearchRepository represents a repository returned by web search
|
||||
type WebSearchRepository struct {
|
||||
Repository *structs.Repository `json:"repository"`
|
||||
LatestCommitStatus *git.CommitStatus `json:"latest_commit_status"`
|
||||
Repository *structs.Repository `json:"repository"`
|
||||
LatestCommitStatus *git.CommitStatus `json:"latest_commit_status"`
|
||||
LocaleLatestCommitStatus string `json:"locale_latest_commit_status"`
|
||||
}
|
||||
|
||||
// WebSearchResults results of a successful web search
|
||||
|
||||
@@ -126,3 +126,27 @@ func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*adm
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// RetryMigrateTask retry a migrate task
|
||||
func RetryMigrateTask(repoID int64) error {
|
||||
migratingTask, err := admin_model.GetMigratingTask(repoID)
|
||||
if err != nil {
|
||||
log.Error("GetMigratingTask: %v", err)
|
||||
return err
|
||||
}
|
||||
if migratingTask.Status == structs.TaskStatusQueued || migratingTask.Status == structs.TaskStatusRunning {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO Need to removing the storage/database garbage brought by the failed task
|
||||
|
||||
// Reset task status and messages
|
||||
migratingTask.Status = structs.TaskStatusQueued
|
||||
migratingTask.Message = ""
|
||||
if err = migratingTask.UpdateCols("status", "message"); err != nil {
|
||||
log.Error("task.UpdateCols failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return taskQueue.Push(migratingTask)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@@ -94,6 +97,67 @@ func TestCreateUser(t *testing.T) {
|
||||
assert.NoError(t, DeleteUser(db.DefaultContext, user, false))
|
||||
}
|
||||
|
||||
func TestRenameUser(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 21})
|
||||
|
||||
t.Run("Non-Local", func(t *testing.T) {
|
||||
u := &user_model.User{
|
||||
Type: user_model.UserTypeIndividual,
|
||||
LoginType: auth.OAuth2,
|
||||
}
|
||||
assert.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename"), user_model.ErrUserIsNotLocal{})
|
||||
})
|
||||
|
||||
t.Run("Same username", func(t *testing.T) {
|
||||
assert.ErrorIs(t, RenameUser(db.DefaultContext, user, user.Name), user_model.ErrUsernameNotChanged{UID: user.ID, Name: user.Name})
|
||||
})
|
||||
|
||||
t.Run("Non usable username", func(t *testing.T) {
|
||||
usernames := []string{"--diff", "aa.png", ".well-known", "search", "aaa.atom"}
|
||||
for _, username := range usernames {
|
||||
t.Run(username, func(t *testing.T) {
|
||||
assert.Error(t, user_model.IsUsableUsername(username))
|
||||
assert.Error(t, RenameUser(db.DefaultContext, user, username))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Only capitalization", func(t *testing.T) {
|
||||
caps := strings.ToUpper(user.Name)
|
||||
unittest.AssertNotExistsBean(t, &user_model.User{ID: user.ID, Name: caps})
|
||||
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, OwnerName: user.Name})
|
||||
|
||||
assert.NoError(t, RenameUser(db.DefaultContext, user, caps))
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID, Name: caps})
|
||||
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, OwnerName: caps})
|
||||
})
|
||||
|
||||
t.Run("Already exists", func(t *testing.T) {
|
||||
existUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
|
||||
assert.ErrorIs(t, RenameUser(db.DefaultContext, user, existUser.Name), user_model.ErrUserAlreadyExist{Name: existUser.Name})
|
||||
assert.ErrorIs(t, RenameUser(db.DefaultContext, user, existUser.LowerName), user_model.ErrUserAlreadyExist{Name: existUser.LowerName})
|
||||
newUsername := fmt.Sprintf("uSEr%d", existUser.ID)
|
||||
assert.ErrorIs(t, RenameUser(db.DefaultContext, user, newUsername), user_model.ErrUserAlreadyExist{Name: newUsername})
|
||||
})
|
||||
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
oldUsername := user.Name
|
||||
newUsername := "User_Rename"
|
||||
|
||||
assert.NoError(t, RenameUser(db.DefaultContext, user, newUsername))
|
||||
unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID, Name: newUsername, LowerName: strings.ToLower(newUsername)})
|
||||
|
||||
redirectUID, err := user_model.LookupUserRedirect(oldUsername)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, user.ID, redirectUID)
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, OwnerName: user.Name})
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateUser_Issue5882(t *testing.T) {
|
||||
// Init settings
|
||||
_ = setting.Admin
|
||||
|
||||
@@ -131,6 +131,10 @@ func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkForm
|
||||
case api.HookIssueReviewed:
|
||||
text = fmt.Sprintf("[%s] Pull request reviewed: %s", repoLink, titleLink)
|
||||
attachmentText = p.Review.Content
|
||||
case api.HookIssueReviewRequested:
|
||||
text = fmt.Sprintf("[%s] Pull request review requested: %s", repoLink, titleLink)
|
||||
case api.HookIssueReviewRequestRemoved:
|
||||
text = fmt.Sprintf("[%s] Pull request review request removed: %s", repoLink, titleLink)
|
||||
}
|
||||
if withSender {
|
||||
text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
|
||||
@@ -256,5 +260,6 @@ func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
|
||||
AuthorizationHeader: authorizationHeader,
|
||||
Updated: w.UpdatedUnix.AsTime(),
|
||||
Created: w.CreatedUnix.AsTime(),
|
||||
BranchFilter: w.BranchFilter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ type (
|
||||
TelegramMeta struct {
|
||||
BotToken string `json:"bot_token"`
|
||||
ChatID string `json:"chat_id"`
|
||||
ThreadID string `json:"thread_id"`
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -111,7 +112,11 @@ func handler(items ...int64) []int64 {
|
||||
for _, taskID := range items {
|
||||
task, err := webhook_model.GetHookTaskByID(ctx, taskID)
|
||||
if err != nil {
|
||||
log.Error("GetHookTaskByID[%d] failed: %v", taskID, err)
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
log.Warn("GetHookTaskByID[%d] warn: %v", taskID, err)
|
||||
} else {
|
||||
log.Error("GetHookTaskByID[%d] failed: %v", taskID, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -22,15 +22,16 @@ import (
|
||||
// - "/wiki/100%25+Free"
|
||||
// - "/wiki/2000-01-02+meeting.-"
|
||||
// - If a segment has a suffix "DashMarker(.-)", it means that there is no dash-space conversion for this segment.
|
||||
// - If a WebPath is a "*.md" pattern, then use it directly as GitPath, to make users can access the raw file.
|
||||
// - If a WebPath is a "*.md" pattern, then use the unescaped value directly as GitPath, to make users can access the raw file.
|
||||
// * Git Path (only space doesn't need to be escaped):
|
||||
// - "/.wiki.git/Home-Page.md"
|
||||
// - "/.wiki.git/100%25 Free.md"
|
||||
// - "/.wiki.git/2000-01-02 meeting.-.md"
|
||||
// TODO: support subdirectory in the future
|
||||
//
|
||||
// Although this package now has the ablity to support subdirectory, but the route package doesn't:
|
||||
// Although this package now has the ability to support subdirectory, but the route package doesn't:
|
||||
// * Double-escaping problem: the URL "/wiki/abc%2Fdef" becomes "/wiki/abc/def" by ctx.Params, which is incorrect
|
||||
// * This problem should have been 99% fixed, but it needs more tests.
|
||||
// * The old wiki code's behavior is always using %2F, instead of subdirectory, so there are a lot of legacy "%2F" files in user wikis.
|
||||
|
||||
type WebPath string
|
||||
@@ -91,7 +92,8 @@ func WebPathSegments(s WebPath) []string {
|
||||
|
||||
func WebPathToGitPath(s WebPath) string {
|
||||
if strings.HasSuffix(string(s), ".md") {
|
||||
return string(s)
|
||||
ret, _ := url.PathUnescape(string(s))
|
||||
return util.PathJoinRelX(ret)
|
||||
}
|
||||
|
||||
a := strings.Split(string(s), "/")
|
||||
@@ -124,7 +126,10 @@ func GitPathToWebPath(s string) (wp WebPath, err error) {
|
||||
func WebPathToUserTitle(s WebPath) (dir, display string) {
|
||||
dir = path.Dir(string(s))
|
||||
display = path.Base(string(s))
|
||||
display = strings.TrimSuffix(display, ".md")
|
||||
if strings.HasSuffix(display, ".md") {
|
||||
display = strings.TrimSuffix(display, ".md")
|
||||
display, _ = url.PathUnescape(display)
|
||||
}
|
||||
display, _ = unescapeSegment(display)
|
||||
return dir, display
|
||||
}
|
||||
@@ -141,8 +146,7 @@ func WebPathFromRequest(s string) WebPath {
|
||||
}
|
||||
|
||||
func UserTitleToWebPath(base, title string) WebPath {
|
||||
// TODO: ctx.Params does un-escaping, so the URL "/wiki/abc%2Fdef" becomes "wiki path = `abc/def`", which is incorrect.
|
||||
// And the old wiki code's behavior is always using %2F, instead of subdirectory.
|
||||
// TODO: no support for subdirectory, because the old wiki code's behavior is always using %2F, instead of subdirectory.
|
||||
// So we do not add the support for writing slashes in title at the moment.
|
||||
title = strings.TrimSpace(title)
|
||||
title = util.PathJoinRelX(base, escapeSegToWeb(title, false))
|
||||
|
||||
@@ -59,6 +59,7 @@ func TestWebPathToDisplayName(t *testing.T) {
|
||||
{"name with / slash", "name-with %2F slash"},
|
||||
{"name with % percent", "name-with %25 percent"},
|
||||
{"2000-01-02 meeting", "2000-01-02+meeting.-.md"},
|
||||
{"a b", "a%20b.md"},
|
||||
} {
|
||||
_, displayName := WebPathToUserTitle(test.WebPath)
|
||||
assert.EqualValues(t, test.Expected, displayName)
|
||||
@@ -73,7 +74,8 @@ func TestWebPathToGitPath(t *testing.T) {
|
||||
for _, test := range []test{
|
||||
{"wiki-name.md", "wiki%20name"},
|
||||
{"wiki-name.md", "wiki+name"},
|
||||
{"wiki%20name.md", "wiki%20name.md"},
|
||||
{"wiki name.md", "wiki%20name.md"},
|
||||
{"wiki%20name.md", "wiki%2520name.md"},
|
||||
{"2000-01-02-meeting.md", "2000-01-02+meeting"},
|
||||
{"2000-01-02 meeting.-.md", "2000-01-02%20meeting.-"},
|
||||
} {
|
||||
@@ -307,3 +309,15 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "Home.md", newWikiPath)
|
||||
}
|
||||
|
||||
func TestWebPathConversion(t *testing.T) {
|
||||
assert.Equal(t, "path/wiki", WebPathToURLPath(WebPath("path/wiki")))
|
||||
assert.Equal(t, "wiki", WebPathToURLPath(WebPath("wiki")))
|
||||
assert.Equal(t, "", WebPathToURLPath(WebPath("")))
|
||||
}
|
||||
|
||||
func TestWebPathFromRequest(t *testing.T) {
|
||||
assert.Equal(t, WebPath("a%2Fb"), WebPathFromRequest("a/b"))
|
||||
assert.Equal(t, WebPath("a"), WebPathFromRequest("a"))
|
||||
assert.Equal(t, WebPath("b"), WebPathFromRequest("a/../b"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user