diff --git a/docs/content/doc/installation/with-docker-rootless.en-us.md b/docs/content/doc/installation/with-docker-rootless.en-us.md index 3cae65c2b2fc..8af3e5b8b66c 100644 --- a/docs/content/doc/installation/with-docker-rootless.en-us.md +++ b/docs/content/doc/installation/with-docker-rootless.en-us.md @@ -29,10 +29,16 @@ the official [install instructions](https://docs.docker.com/compose/install/). The most simple setup just creates a volume and a network and starts the `gitea/gitea:latest-rootless` image as a service. Since there is no database available, one can be initialized using SQLite3. -Create a directory for `data` and `config` then paste the following content into a file named `docker-compose.yml`. -Note that the volume should be owned by the user/group with the UID/GID specified in the config file. By default Gitea in docker will use uid:1000 gid:1000. If needed you can set ownership on those folders with the command: `sudo chown 1000:1000 config/ data/` -If you don't give the volume correct permissions, the container may not start. -For a stable release you could use `:latest-rootless`, `:1-rootless` or specify a certain release like `:{{< version >}}-rootless`, but if you'd like to use the latest development version then `:dev-rootless` would be an appropriate tag. If you'd like to run the latest commit from a release branch you can use the `:1.x-dev-rootless` tag, where x is the minor version of Gitea. (e.g. `:1.16-dev-rootless`) + +Create a directory for `data` and `config`: + +```sh +mkdir -p gitea/{data,config} +cd gitea +touch docker-compose.yml +``` + +Then paste the following content into a file named `docker-compose.yml`: ```yaml version: "2" @@ -51,6 +57,16 @@ services: - "2222:2222" ``` +Note that the volume should be owned by the user/group with the UID/GID specified in the config file. By default Gitea in docker will use uid:1000 gid:1000. If needed you can set ownership on those folders with the command: + +```sh +sudo chown 1000:1000 config/ data/ +``` + +> If you don't give the volume correct permissions, the container may not start. + +For a stable release you could use `:latest-rootless`, `:1-rootless` or specify a certain release like `:{{< version >}}-rootless`, but if you'd like to use the latest development version then `:dev-rootless` would be an appropriate tag. If you'd like to run the latest commit from a release branch you can use the `:1.x-dev-rootless` tag, where x is the minor version of Gitea. (e.g. `:1.16-dev-rootless`) + ## Custom port To bind the integrated ssh and the webserver on a different port, adjust diff --git a/models/auth/token.go b/models/auth/token.go index 17c07531f85a..9902b80b7826 100644 --- a/models/auth/token.go +++ b/models/auth/token.go @@ -66,7 +66,7 @@ type AccessToken struct { Token string `xorm:"-"` TokenHash string `xorm:"UNIQUE"` // sha256 of token TokenSalt string - TokenLastEight string `xorm:"token_last_eight"` + TokenLastEight string `xorm:"INDEX token_last_eight"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` diff --git a/models/avatars/avatar.go b/models/avatars/avatar.go index 61f0ac19c311..dec264cea426 100644 --- a/models/avatars/avatar.go +++ b/models/avatars/avatar.go @@ -20,8 +20,12 @@ import ( "code.gitea.io/gitea/modules/setting" ) -// DefaultAvatarPixelSize is the default size in pixels of a rendered avatar -const DefaultAvatarPixelSize = 28 +const ( + // DefaultAvatarClass is the default class of a rendered avatar + DefaultAvatarClass = "ui avatar vm" + // DefaultAvatarPixelSize is the default size in pixels of a rendered avatar + DefaultAvatarPixelSize = 28 +) // EmailHash represents a pre-generated hash map (mainly used by LibravatarURL, it queries email server's DNS records) type EmailHash struct { diff --git a/models/migrations/fixtures/Test_addConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml b/models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml similarity index 100% rename from models/migrations/fixtures/Test_addConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml rename to models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml diff --git a/models/migrations/fixtures/Test_addHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml b/models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml similarity index 100% rename from models/migrations/fixtures/Test_addHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml rename to models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/expected_webhook.yml diff --git a/models/migrations/fixtures/Test_addHeaderAuthorizationEncryptedColWebhook/hook_task.yml b/models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/hook_task.yml similarity index 100% rename from models/migrations/fixtures/Test_addHeaderAuthorizationEncryptedColWebhook/hook_task.yml rename to models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/hook_task.yml diff --git a/models/migrations/fixtures/Test_addHeaderAuthorizationEncryptedColWebhook/webhook.yml b/models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/webhook.yml similarity index 100% rename from models/migrations/fixtures/Test_addHeaderAuthorizationEncryptedColWebhook/webhook.yml rename to models/migrations/fixtures/Test_AddHeaderAuthorizationEncryptedColWebhook/webhook.yml diff --git a/models/migrations/fixtures/Test_deleteOrphanedIssueLabels/issue_label.yml b/models/migrations/fixtures/Test_DeleteOrphanedIssueLabels/issue_label.yml similarity index 100% rename from models/migrations/fixtures/Test_deleteOrphanedIssueLabels/issue_label.yml rename to models/migrations/fixtures/Test_DeleteOrphanedIssueLabels/issue_label.yml diff --git a/models/migrations/fixtures/Test_deleteOrphanedIssueLabels/label.yml b/models/migrations/fixtures/Test_DeleteOrphanedIssueLabels/label.yml similarity index 100% rename from models/migrations/fixtures/Test_deleteOrphanedIssueLabels/label.yml rename to models/migrations/fixtures/Test_DeleteOrphanedIssueLabels/label.yml diff --git a/models/migrations/fixtures/Test_remigrateU2FCredentials/expected_webauthn_credential.yml b/models/migrations/fixtures/Test_RemigrateU2FCredentials/expected_webauthn_credential.yml similarity index 100% rename from models/migrations/fixtures/Test_remigrateU2FCredentials/expected_webauthn_credential.yml rename to models/migrations/fixtures/Test_RemigrateU2FCredentials/expected_webauthn_credential.yml diff --git a/models/migrations/fixtures/Test_remigrateU2FCredentials/u2f_registration.yml b/models/migrations/fixtures/Test_RemigrateU2FCredentials/u2f_registration.yml similarity index 100% rename from models/migrations/fixtures/Test_remigrateU2FCredentials/u2f_registration.yml rename to models/migrations/fixtures/Test_RemigrateU2FCredentials/u2f_registration.yml diff --git a/models/migrations/fixtures/Test_remigrateU2FCredentials/webauthn_credential.yml b/models/migrations/fixtures/Test_RemigrateU2FCredentials/webauthn_credential.yml similarity index 100% rename from models/migrations/fixtures/Test_remigrateU2FCredentials/webauthn_credential.yml rename to models/migrations/fixtures/Test_RemigrateU2FCredentials/webauthn_credential.yml diff --git a/models/migrations/fixtures/Test_removeInvalidLabels/comment.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/comment.yml similarity index 100% rename from models/migrations/fixtures/Test_removeInvalidLabels/comment.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/comment.yml diff --git a/models/migrations/fixtures/Test_removeInvalidLabels/issue.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/issue.yml similarity index 100% rename from models/migrations/fixtures/Test_removeInvalidLabels/issue.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/issue.yml diff --git a/models/migrations/fixtures/Test_removeInvalidLabels/issue_label.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/issue_label.yml similarity index 100% rename from models/migrations/fixtures/Test_removeInvalidLabels/issue_label.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/issue_label.yml diff --git a/models/migrations/fixtures/Test_removeInvalidLabels/label.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/label.yml similarity index 100% rename from models/migrations/fixtures/Test_removeInvalidLabels/label.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/label.yml diff --git a/models/migrations/fixtures/Test_removeInvalidLabels/repository.yml b/models/migrations/fixtures/Test_RemoveInvalidLabels/repository.yml similarity index 100% rename from models/migrations/fixtures/Test_removeInvalidLabels/repository.yml rename to models/migrations/fixtures/Test_RemoveInvalidLabels/repository.yml diff --git a/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml b/models/migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml similarity index 100% rename from models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml rename to models/migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml diff --git a/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/webauthn_credential.yml b/models/migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/webauthn_credential.yml similarity index 100% rename from models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/webauthn_credential.yml rename to models/migrations/fixtures/Test_StoreWebauthnCredentialIDAsBytes/webauthn_credential.yml diff --git a/models/migrations/fixtures/Test_unwrapLDAPSourceCfg/login_source.yml b/models/migrations/fixtures/Test_UnwrapLDAPSourceCfg/login_source.yml similarity index 100% rename from models/migrations/fixtures/Test_unwrapLDAPSourceCfg/login_source.yml rename to models/migrations/fixtures/Test_UnwrapLDAPSourceCfg/login_source.yml diff --git a/models/migrations/fixtures/Test_updateOpenMilestoneCounts/expected_milestone.yml b/models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/expected_milestone.yml similarity index 100% rename from models/migrations/fixtures/Test_updateOpenMilestoneCounts/expected_milestone.yml rename to models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/expected_milestone.yml diff --git a/models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml b/models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/issue.yml similarity index 100% rename from models/migrations/fixtures/Test_updateOpenMilestoneCounts/issue.yml rename to models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/issue.yml diff --git a/models/migrations/fixtures/Test_updateOpenMilestoneCounts/milestone.yml b/models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/milestone.yml similarity index 100% rename from models/migrations/fixtures/Test_updateOpenMilestoneCounts/milestone.yml rename to models/migrations/fixtures/Test_UpdateOpenMilestoneCounts/milestone.yml diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index c48fc8d9a8a9..a48c837e8b25 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -441,6 +441,8 @@ var migrations = []Migration{ NewMigration("Add header_authorization_encrypted column to webhook table", v1_19.AddHeaderAuthorizationEncryptedColWebhook), // v234 -> v235 NewMigration("Add package cleanup rule table", v1_19.CreatePackageCleanupRuleTable), + // v235 -> v236 + NewMigration("Add index for access_token", v1_19.AddIndexForAccessToken), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go new file mode 100644 index 000000000000..b43ee32a56e1 --- /dev/null +++ b/models/migrations/v1_19/v235.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package v1_19 //nolint + +import ( + "xorm.io/xorm" +) + +func AddIndexForAccessToken(x *xorm.Engine) error { + type AccessToken struct { + TokenLastEight string `xorm:"INDEX token_last_eight"` + } + + return x.Sync(new(AccessToken)) +} diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go index 246484aea96b..92d9e9738333 100644 --- a/models/webhook/hooktask.go +++ b/models/webhook/hooktask.go @@ -233,14 +233,30 @@ func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask, return newTask, db.Insert(ctx, newTask) } -// FindUndeliveredHookTasks represents find the undelivered hook tasks -func FindUndeliveredHookTasks(ctx context.Context) ([]*HookTask, error) { - tasks := make([]*HookTask, 0, 10) +// FindUndeliveredHookTaskIDs will find the next 100 undelivered hook tasks with ID greater than the provided lowerID +func FindUndeliveredHookTaskIDs(ctx context.Context, lowerID int64) ([]int64, error) { + const batchSize = 100 + + tasks := make([]int64, 0, batchSize) return tasks, db.GetEngine(ctx). + Select("id"). + Table(new(HookTask)). Where("is_delivered=?", false). + And("id > ?", lowerID). + Asc("id"). + Limit(batchSize). Find(&tasks) } +func MarkTaskDelivered(ctx context.Context, task *HookTask) (bool, error) { + count, err := db.GetEngine(ctx).ID(task.ID).Where("is_delivered = ?", false).Cols("is_delivered").Update(&HookTask{ + ID: task.ID, + IsDelivered: true, + }) + + return count != 0, err +} + // CleanupHookTaskTable deletes rows from hook_task as needed. func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType, olderThan time.Duration, numberToKeep int) error { log.Trace("Doing: CleanupHookTaskTable") diff --git a/modules/templates/helper.go b/modules/templates/helper.go index d0866d3e2c46..95671e791a4e 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -607,7 +607,7 @@ func AvatarHTML(src string, size int, class, name string) template.HTML { // Avatar renders user avatars. args: user, size (int), class (string) func Avatar(item interface{}, others ...interface{}) template.HTML { - size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, "ui avatar vm", others...) + size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) switch t := item.(type) { case *user_model.User: @@ -638,7 +638,7 @@ func AvatarByAction(action *activities_model.Action, others ...interface{}) temp // RepoAvatar renders repo avatars. args: repo, size(int), class (string) func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML { - size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, "ui avatar", others...) + size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) src := repo.RelAvatarLink() if src != "" { @@ -649,7 +649,7 @@ func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTM // AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string) func AvatarByEmail(email, name string, others ...interface{}) template.HTML { - size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, "ui avatar", others...) + size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) src := avatars.GenerateEmailAvatarFastLink(email, size*setting.Avatar.RenderedSizeFactor) if src != "" { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 02598dc3dc4d..8503cb78d712 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1630,6 +1630,8 @@ pulls.reopened_at = `reopened this pull request %[2] pulls.merge_instruction_hint = `You can also view command line instructions.` pulls.merge_instruction_step1_desc = From your project repository, check out a new branch and test the changes. pulls.merge_instruction_step2_desc = Merge the changes and update on Gitea. +pulls.clear_merge_message = Clear merge message +pulls.clear_merge_message_hint = Clearing the merge message will only remove the commit message content and keep generated git trailers such as "Co-Authored-By …". pulls.auto_merge_button_when_succeed = (When checks succeed) pulls.auto_merge_when_succeed = Auto merge when all checks succeed diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index 85717e09783e..07fdf18c8347 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -23,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/proxy" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" @@ -43,7 +44,7 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { return } // There was a panic whilst delivering a hook... - log.Error("PANIC whilst trying to deliver webhook[%d] to %s Panic: %v\nStacktrace: %s", t.ID, w.URL, err, log.Stack(2)) + log.Error("PANIC whilst trying to deliver webhook task[%d] to webhook %s Panic: %v\nStacktrace: %s", t.ID, w.URL, err, log.Stack(2)) }() t.IsDelivered = true @@ -52,7 +53,7 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { switch w.HTTPMethod { case "": - log.Info("HTTP Method for webhook %d empty, setting to POST as default", t.ID) + log.Info("HTTP Method for webhook %s empty, setting to POST as default", w.URL) fallthrough case http.MethodPost: switch w.ContentType { @@ -78,14 +79,14 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { case http.MethodGet: u, err := url.Parse(w.URL) if err != nil { - return err + return fmt.Errorf("unable to deliver webhook task[%d] as cannot parse webhook url %s: %w", t.ID, w.URL, err) } vals := u.Query() vals["payload"] = []string{t.PayloadContent} u.RawQuery = vals.Encode() req, err = http.NewRequest("GET", u.String(), nil) if err != nil { - return err + return fmt.Errorf("unable to deliver webhook task[%d] as unable to create HTTP request for webhook url %s: %w", t.ID, w.URL, err) } case http.MethodPut: switch w.Type { @@ -97,13 +98,13 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID)) req, err = http.NewRequest("PUT", url, strings.NewReader(t.PayloadContent)) if err != nil { - return err + return fmt.Errorf("unable to deliver webhook task[%d] as cannot create matrix request for webhook url %s: %w", t.ID, w.URL, err) } default: - return fmt.Errorf("invalid http method for webhook: [%d] %v", t.ID, w.HTTPMethod) + return fmt.Errorf("invalid http method for webhook task[%d] in webhook %s: %v", t.ID, w.URL, w.HTTPMethod) } default: - return fmt.Errorf("invalid http method for webhook: [%d] %v", t.ID, w.HTTPMethod) + return fmt.Errorf("invalid http method for webhook task[%d] in webhook %s: %v", t.ID, w.URL, w.HTTPMethod) } var signatureSHA1 string @@ -159,6 +160,20 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { Headers: map[string]string{}, } + // OK We're now ready to attempt to deliver the task - we must double check that it + // has not been delivered in the meantime + updated, err := webhook_model.MarkTaskDelivered(ctx, t) + if err != nil { + log.Error("MarkTaskDelivered[%d]: %v", t.ID, err) + return fmt.Errorf("unable to mark task[%d] delivered in the db: %w", t.ID, err) + } + if !updated { + // This webhook task has already been attempted to be delivered or is in the process of being delivered + log.Trace("Webhook Task[%d] already delivered", t.ID) + return nil + } + + // All code from this point will update the hook task defer func() { t.Delivered = time.Now().UnixNano() if t.IsSucceed { @@ -190,13 +205,14 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { } if !w.IsActive { + log.Trace("Webhook %s in Webhook Task[%d] is not active", w.URL, t.ID) return nil } resp, err := webhookHTTPClient.Do(req.WithContext(ctx)) if err != nil { t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err) - return err + return fmt.Errorf("unable to deliver webhook task[%d] in %s due to error in http client: %w", t.ID, w.URL, err) } defer resp.Body.Close() @@ -210,7 +226,7 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { p, err := io.ReadAll(resp.Body) if err != nil { t.ResponseInfo.Body = fmt.Sprintf("read body: %s", err) - return err + return fmt.Errorf("unable to deliver webhook task[%d] in %s as unable to read response body: %w", t.ID, w.URL, err) } t.ResponseInfo.Body = string(p) return nil @@ -272,17 +288,37 @@ func Init() error { } go graceful.GetManager().RunWithShutdownFns(hookQueue.Run) - tasks, err := webhook_model.FindUndeliveredHookTasks(graceful.GetManager().HammerContext()) - if err != nil { - log.Error("FindUndeliveredHookTasks failed: %v", err) - return err - } + go graceful.GetManager().RunWithShutdownContext(populateWebhookSendingQueue) + + return nil +} + +func populateWebhookSendingQueue(ctx context.Context) { + ctx, _, finished := process.GetManager().AddContext(ctx, "Webhook: Populate sending queue") + defer finished() - for _, task := range tasks { - if err := enqueueHookTask(task); err != nil { - log.Error("enqueueHookTask failed: %v", err) + lowerID := int64(0) + for { + taskIDs, err := webhook_model.FindUndeliveredHookTaskIDs(ctx, lowerID) + if err != nil { + log.Error("Unable to populate webhook queue as FindUndeliveredHookTaskIDs failed: %v", err) + return + } + if len(taskIDs) == 0 { + return + } + lowerID = taskIDs[len(taskIDs)-1] + + for _, taskID := range taskIDs { + select { + case <-ctx.Done(): + log.Warn("Shutdown before Webhook Sending queue finishing being populated") + return + default: + } + if err := enqueueHookTask(taskID); err != nil { + log.Error("Unable to push HookTask[%d] to the Webhook Sending queue: %v", taskID, err) + } } } - - return nil } diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index 83ca7d61786d..498cf7d159f0 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/models/unittest" webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) @@ -67,8 +68,15 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) { err := hook.SetHeaderAuthorization("Bearer s3cr3t-t0ken") assert.NoError(t, err) assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook)) + db.GetEngine(db.DefaultContext).NoAutoTime().DB().Logger.ShowSQL(true) - hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_model.HookEventPush} + hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_model.HookEventPush, Payloader: &api.PushPayload{}} + + hookTask, err = webhook_model.CreateHookTask(db.DefaultContext, hookTask) + assert.NoError(t, err) + if !assert.NotNil(t, hookTask) { + return + } assert.NoError(t, Deliver(context.Background(), hookTask)) select { diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index 1780022eb4b2..5c9139b41f1c 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -116,19 +116,26 @@ func handle(data ...queue.Data) []queue.Data { for _, taskID := range data { task, err := webhook_model.GetHookTaskByID(ctx, taskID.(int64)) if err != nil { - log.Error("GetHookTaskByID failed: %v", err) - } else { - if err := Deliver(ctx, task); err != nil { - log.Error("webhook.Deliver failed: %v", err) - } + log.Error("GetHookTaskByID[%d] failed: %v", taskID.(int64), err) + continue + } + + if task.IsDelivered { + // Already delivered in the meantime + log.Trace("Task[%d] has already been delivered", task.ID) + continue + } + + if err := Deliver(ctx, task); err != nil { + log.Error("Unable to deliver webhook task[%d]: %v", task.ID, err) } } return nil } -func enqueueHookTask(task *webhook_model.HookTask) error { - err := hookQueue.PushFunc(task.ID, nil) +func enqueueHookTask(taskID int64) error { + err := hookQueue.Push(taskID) if err != nil && err != queue.ErrAlreadyInQueue { return err } @@ -205,7 +212,7 @@ func PrepareWebhook(ctx context.Context, w *webhook_model.Webhook, event webhook return fmt.Errorf("CreateHookTask: %w", err) } - return enqueueHookTask(task) + return enqueueHookTask(task.ID) } // PrepareWebhooks adds new webhooks to task queue for given payload. @@ -265,5 +272,5 @@ func ReplayHookTask(ctx context.Context, w *webhook_model.Webhook, uuid string) return err } - return enqueueHookTask(task) + return enqueueHookTask(task.ID) } diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl index 044af9522e6e..ad03ff800f81 100644 --- a/templates/explore/repo_list.tmpl +++ b/templates/explore/repo_list.tmpl @@ -32,9 +32,9 @@ {{end}} {{end}} {{if .IsFork}} - {{svg "octicon-repo-forked"}} + {{svg "octicon-repo-forked"}} {{else if .IsMirror}} - {{svg "octicon-mirror"}} + {{svg "octicon-mirror"}} {{end}} diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 1e07225300bb..9e0909064de1 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -351,6 +351,8 @@ 'textAutoMergeButtonWhenSucceed': {{$.locale.Tr "repo.pulls.auto_merge_button_when_succeed"}}, 'textAutoMergeWhenSucceed': {{$.locale.Tr "repo.pulls.auto_merge_when_succeed"}}, 'textAutoMergeCancelSchedule': {{$.locale.Tr "repo.pulls.auto_merge_cancel_schedule"}}, + 'textClearMergeMessage': {{$.locale.Tr "repo.pulls.clear_merge_message"}}, + 'textClearMergeMessageHint': {{$.locale.Tr "repo.pulls.clear_merge_message_hint"}}, 'canMergeNow': {{$canMergeNow}}, 'allOverridableChecksOk': {{not $notAllOverridableChecksOk}}, @@ -360,6 +362,7 @@ 'defaultMergeStyle': {{.MergeStyle}}, 'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}}, 'mergeMessageFieldPlaceHolder': {{$.locale.Tr "repo.editor.commit_message_desc"}}, + 'defaultMergeMessage': defaultMergeMessage, 'hasPendingPullRequestMerge': {{.HasPendingPullRequestMerge}}, 'hasPendingPullRequestMergeTip': {{$hasPendingPullRequestMergeTip}}, diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue index bfe05628e84a..1fec12dd5a0a 100644 --- a/web_src/js/components/PullRequestMergeForm.vue +++ b/web_src/js/components/PullRequestMergeForm.vue @@ -25,6 +25,14 @@