Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Bence Santha 2024-09-20 14:15:56 +02:00
commit 35bb7e7ed5
42 changed files with 372 additions and 435 deletions

5
go.mod
View File

@ -2,6 +2,11 @@ module code.gitea.io/gitea
go 1.23 go 1.23
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
// But some CAs use negative serial number, just relax the check. related:
// Default TLS cert uses negative serial number #895 https://github.com/microsoft/mssql-docker/issues/895
godebug x509negativeserial=1
require ( require (
code.gitea.io/actions-proto-go v0.4.0 code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/gitea-vet v0.2.3 code.gitea.io/gitea-vet v0.2.3

View File

@ -247,7 +247,7 @@ func (r *Review) TooltipContent() string {
} }
return "repo.issues.review.official" return "repo.issues.review.official"
case ReviewTypeComment: case ReviewTypeComment:
return "repo.issues.review.comment" return "repo.issues.review.commented"
case ReviewTypeReject: case ReviewTypeReject:
return "repo.issues.review.rejected" return "repo.issues.review.rejected"
case ReviewTypeRequest: case ReviewTypeRequest:

View File

@ -234,6 +234,7 @@ type FindReleasesOptions struct {
IsDraft optional.Option[bool] IsDraft optional.Option[bool]
TagNames []string TagNames []string
HasSha1 optional.Option[bool] // useful to find draft releases which are created with existing tags HasSha1 optional.Option[bool] // useful to find draft releases which are created with existing tags
NamePattern optional.Option[string]
} }
func (opts FindReleasesOptions) ToConds() builder.Cond { func (opts FindReleasesOptions) ToConds() builder.Cond {
@ -261,6 +262,11 @@ func (opts FindReleasesOptions) ToConds() builder.Cond {
cond = cond.And(builder.Eq{"sha1": ""}) cond = cond.And(builder.Eq{"sha1": ""})
} }
} }
if opts.NamePattern.Has() && opts.NamePattern.Value() != "" {
cond = cond.And(builder.Like{"lower_tag_name", strings.ToLower(opts.NamePattern.Value())})
}
return cond return cond
} }

View File

@ -34,7 +34,7 @@ func AvatarHTML(src string, size int, class, name string) template.HTML {
name = "avatar" name = "avatar"
} }
return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`) return template.HTML(`<img loading="lazy" class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
} }
// Avatar renders user avatars. args: user, size (int), class (string) // Avatar renders user avatars. args: user, size (int), class (string)

View File

@ -218,8 +218,6 @@ string.desc=Z A
[error] [error]
occurred=Došlo k chybě occurred=Došlo k chybě
missing_csrf=Špatný požadavek: Neexistuje CSRF token
invalid_csrf=Špatný požadavek: Neplatný CSRF token
not_found=Cíl nebyl nalezen. not_found=Cíl nebyl nalezen.
network_error=Chyba sítě network_error=Chyba sítě
@ -1701,7 +1699,6 @@ issues.dependency.add_error_dep_not_same_repo=Oba úkoly musí být ve stejném
issues.review.self.approval=Nemůžete schválit svůj pull request. issues.review.self.approval=Nemůžete schválit svůj pull request.
issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním pull requestu. issues.review.self.rejection=Nemůžete požadovat změny ve svém vlastním pull requestu.
issues.review.approve=schválil tyto změny %s issues.review.approve=schválil tyto změny %s
issues.review.comment=Okomentovat
issues.review.dismissed=zamítl/a posouzení od %s %s issues.review.dismissed=zamítl/a posouzení od %s %s
issues.review.dismissed_label=Zamítnuto issues.review.dismissed_label=Zamítnuto
issues.review.left_comment=zanechal komentář issues.review.left_comment=zanechal komentář
@ -1726,6 +1723,7 @@ issues.review.hide_resolved=Skrýt vyřešené
issues.review.resolve_conversation=Vyřešit konverzaci issues.review.resolve_conversation=Vyřešit konverzaci
issues.review.un_resolve_conversation=Nevyřešit konverzaci issues.review.un_resolve_conversation=Nevyřešit konverzaci
issues.review.resolved_by=označil tuto konverzaci jako vyřešenou issues.review.resolved_by=označil tuto konverzaci jako vyřešenou
issues.review.commented=Okomentovat
issues.assignee.error=Ne všichni zpracovatelé byli přidáni z důvodu neočekávané chyby. issues.assignee.error=Ne všichni zpracovatelé byli přidáni z důvodu neočekávané chyby.
issues.reference_issue.body=Tělo zprávy issues.reference_issue.body=Tělo zprávy
issues.content_history.deleted=vymazáno issues.content_history.deleted=vymazáno

View File

@ -213,8 +213,6 @@ string.desc=ZA
[error] [error]
occurred=Ein Fehler ist aufgetreten occurred=Ein Fehler ist aufgetreten
missing_csrf=Fehlerhafte Anfrage: Kein CSRF Token verfügbar
invalid_csrf=Fehlerhafte Anfrage: Ungültiger CSRF Token
not_found=Das Ziel konnte nicht gefunden werden. not_found=Das Ziel konnte nicht gefunden werden.
network_error=Netzwerkfehler network_error=Netzwerkfehler
@ -1681,7 +1679,6 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe
issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen.
issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen.
issues.review.approve=hat die Änderungen %s genehmigt issues.review.approve=hat die Änderungen %s genehmigt
issues.review.comment=Kommentieren
issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed=verwarf %ss Review %s
issues.review.dismissed_label=Verworfen issues.review.dismissed_label=Verworfen
issues.review.left_comment=hat einen Kommentar hinterlassen issues.review.left_comment=hat einen Kommentar hinterlassen
@ -1706,6 +1703,7 @@ issues.review.hide_resolved=Gelöste ausblenden
issues.review.resolve_conversation=Diskussion als "erledigt" markieren issues.review.resolve_conversation=Diskussion als "erledigt" markieren
issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren
issues.review.resolved_by=markierte diese Unterhaltung als gelöst issues.review.resolved_by=markierte diese Unterhaltung als gelöst
issues.review.commented=Kommentieren
issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden. issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden.
issues.reference_issue.body=Beschreibung issues.reference_issue.body=Beschreibung
issues.content_history.deleted=gelöscht issues.content_history.deleted=gelöscht

View File

@ -184,8 +184,6 @@ string.desc=Z - A
[error] [error]
occurred=Παρουσιάστηκε ένα σφάλμα occurred=Παρουσιάστηκε ένα σφάλμα
missing_csrf=Bad Request: δεν υπάρχει διακριτικό CSRF
invalid_csrf=Λάθος Αίτημα: μη έγκυρο διακριτικό CSRF
not_found=Ο προορισμός δεν βρέθηκε. not_found=Ο προορισμός δεν βρέθηκε.
network_error=Σφάλμα δικτύου network_error=Σφάλμα δικτύου
@ -1603,7 +1601,6 @@ issues.dependency.add_error_dep_not_same_repo=Και τα δύο ζητήματ
issues.review.self.approval=Δεν μπορείτε να εγκρίνετε το δικό σας pull request. issues.review.self.approval=Δεν μπορείτε να εγκρίνετε το δικό σας pull request.
issues.review.self.rejection=Δεν μπορείτε να ζητήσετε αλλαγές στο δικό σας pull request. issues.review.self.rejection=Δεν μπορείτε να ζητήσετε αλλαγές στο δικό σας pull request.
issues.review.approve=ενέκρινε αυτές τις αλλαγές %s issues.review.approve=ενέκρινε αυτές τις αλλαγές %s
issues.review.comment=Σχόλιο
issues.review.dismissed=απέρριψε την αξιολόγηση %s %s issues.review.dismissed=απέρριψε την αξιολόγηση %s %s
issues.review.dismissed_label=Απορρίφθηκε issues.review.dismissed_label=Απορρίφθηκε
issues.review.left_comment=άφησε ένα σχόλιο issues.review.left_comment=άφησε ένα σχόλιο
@ -1628,6 +1625,7 @@ issues.review.hide_resolved=Απόκρυψη επιλυμένων
issues.review.resolve_conversation=Επίλυση συνομιλίας issues.review.resolve_conversation=Επίλυση συνομιλίας
issues.review.un_resolve_conversation=Ανεπίλυτη συνομιλία issues.review.un_resolve_conversation=Ανεπίλυτη συνομιλία
issues.review.resolved_by=σημείωση αυτή την συνομιλία ως επιλυμένη issues.review.resolved_by=σημείωση αυτή την συνομιλία ως επιλυμένη
issues.review.commented=Σχόλιο
issues.assignee.error=Δεν προστέθηκαν όλοι οι παραλήπτες λόγω απροσδόκητου σφάλματος. issues.assignee.error=Δεν προστέθηκαν όλοι οι παραλήπτες λόγω απροσδόκητου σφάλματος.
issues.reference_issue.body=Σώμα issues.reference_issue.body=Σώμα
issues.content_history.deleted=διαγράφηκε issues.content_history.deleted=διαγράφηκε

View File

@ -178,6 +178,8 @@ code_search_by_git_grep = Current code search results are provided by "git grep"
package_kind = Search packages... package_kind = Search packages...
project_kind = Search projects... project_kind = Search projects...
branch_kind = Search branches... branch_kind = Search branches...
tag_kind = Search tags...
tag_tooltip = Search for matching tags. Use '%' to match any sequence of numbers.
commit_kind = Search commits... commit_kind = Search commits...
runner_kind = Search runners... runner_kind = Search runners...
no_results = No matching results found. no_results = No matching results found.
@ -220,8 +222,6 @@ string.desc = Z - A
[error] [error]
occurred = An error occurred occurred = An error occurred
report_message = If you believe that this is a Gitea bug, please search for issues on <a href="%s" target="_blank">GitHub</a> or open a new issue if necessary. report_message = If you believe that this is a Gitea bug, please search for issues on <a href="%s" target="_blank">GitHub</a> or open a new issue if necessary.
missing_csrf = Bad Request: no CSRF token present
invalid_csrf = Bad Request: invalid CSRF token
not_found = The target couldn't be found. not_found = The target couldn't be found.
network_error = Network error network_error = Network error
@ -1757,7 +1757,7 @@ issues.review.hide_resolved = Hide resolved
issues.review.resolve_conversation = Resolve conversation issues.review.resolve_conversation = Resolve conversation
issues.review.un_resolve_conversation = Unresolve conversation issues.review.un_resolve_conversation = Unresolve conversation
issues.review.resolved_by = marked this conversation as resolved issues.review.resolved_by = marked this conversation as resolved
issues.review.comment = Comment issues.review.commented = Comment
issues.review.official = Approved issues.review.official = Approved
issues.review.requested = Review pending issues.review.requested = Review pending
issues.review.rejected = Changes requested issues.review.rejected = Changes requested

View File

@ -182,8 +182,6 @@ string.desc=Z - A
[error] [error]
occurred=Ha ocurrido un error occurred=Ha ocurrido un error
missing_csrf=Solicitud incorrecta: sin token CSRF
invalid_csrf=Solicitud incorrecta: el token CSRF no es válido
not_found=El objetivo no pudo ser encontrado. not_found=El objetivo no pudo ser encontrado.
network_error=Error de red network_error=Error de red
@ -1593,7 +1591,6 @@ issues.dependency.add_error_dep_not_same_repo=Ambas incidencias deben estar en e
issues.review.self.approval=No puede aprobar su propio pull request. issues.review.self.approval=No puede aprobar su propio pull request.
issues.review.self.rejection=No puede sugerir cambios en su propio pull request. issues.review.self.rejection=No puede sugerir cambios en su propio pull request.
issues.review.approve=aprobado estos cambios %s issues.review.approve=aprobado estos cambios %s
issues.review.comment=Comentario
issues.review.dismissed=descartó la revisión de %s %s issues.review.dismissed=descartó la revisión de %s %s
issues.review.dismissed_label=Descartado issues.review.dismissed_label=Descartado
issues.review.left_comment=dejó un comentario issues.review.left_comment=dejó un comentario
@ -1618,6 +1615,7 @@ issues.review.hide_resolved=Ocultar resueltos
issues.review.resolve_conversation=Resolver conversación issues.review.resolve_conversation=Resolver conversación
issues.review.un_resolve_conversation=Marcar conversación sin resolver issues.review.un_resolve_conversation=Marcar conversación sin resolver
issues.review.resolved_by=ha marcado esta conversación como resuelta issues.review.resolved_by=ha marcado esta conversación como resuelta
issues.review.commented=Comentario
issues.assignee.error=No todos los asignados fueron añadidos debido a un error inesperado. issues.assignee.error=No todos los asignados fueron añadidos debido a un error inesperado.
issues.reference_issue.body=Cuerpo issues.reference_issue.body=Cuerpo
issues.content_history.deleted=borrado issues.content_history.deleted=borrado

View File

@ -118,7 +118,6 @@ filter.private=خصوصی
[filter] [filter]
[error] [error]
missing_csrf=درخواست بد: بلیط CSRF ندارد
[startpage] [startpage]
app_desc=یک سرویس گیت بی‌درد سر و راحت app_desc=یک سرویس گیت بی‌درد سر و راحت
@ -1235,7 +1234,6 @@ issues.dependency.add_error_dep_not_same_repo=هر دو موضوع باید از
issues.review.self.approval=شما نمی‌توانید تقاضای واکشی خود را تایید کنید. issues.review.self.approval=شما نمی‌توانید تقاضای واکشی خود را تایید کنید.
issues.review.self.rejection=شما نمی‌توانید تقاضا تغییرات تقاضای واکشی خود را تغییر دهید. issues.review.self.rejection=شما نمی‌توانید تقاضا تغییرات تقاضای واکشی خود را تغییر دهید.
issues.review.approve=این تغییرات را تایید شدند %s issues.review.approve=این تغییرات را تایید شدند %s
issues.review.comment=دیدگاه
issues.review.dismissed=بررسی %s %s را رد شده issues.review.dismissed=بررسی %s %s را رد شده
issues.review.dismissed_label=رها شده issues.review.dismissed_label=رها شده
issues.review.left_comment=یک نظر ثبت کرد issues.review.left_comment=یک نظر ثبت کرد
@ -1256,6 +1254,7 @@ issues.review.hide_resolved=مخفی کردن حل شده ها
issues.review.resolve_conversation=مکالمه را بعنوان حل شده علامت گذاری کردن issues.review.resolve_conversation=مکالمه را بعنوان حل شده علامت گذاری کردن
issues.review.un_resolve_conversation=مکالمه را بعنوان حل نشده علامت گذاری کردن issues.review.un_resolve_conversation=مکالمه را بعنوان حل نشده علامت گذاری کردن
issues.review.resolved_by=علامت گذاری این مکالمه بعنوان حل شده issues.review.resolved_by=علامت گذاری این مکالمه بعنوان حل شده
issues.review.commented=دیدگاه
issues.assignee.error=به دلیل خطای غیرمنتظره همه تکالیف اضافه نشد. issues.assignee.error=به دلیل خطای غیرمنتظره همه تکالیف اضافه نشد.
issues.reference_issue.body=Body issues.reference_issue.body=Body
issues.content_history.deleted=حذف شده issues.content_history.deleted=حذف شده

View File

@ -133,8 +133,6 @@ filter.private=Yksityinen
[error] [error]
occurred=Virhe tapahtui occurred=Virhe tapahtui
missing_csrf=Virheellinen pyyntö: CSRF-tunnusta ei ole olemassa
invalid_csrf=Virheellinen pyyntö: Virheellinen CSRF-tunniste
not_found=Kohdetta ei löytynyt. not_found=Kohdetta ei löytynyt.
network_error=Verkkovirhe network_error=Verkkovirhe
@ -955,6 +953,7 @@ issues.review.left_comment=jätti kommentin
issues.review.pending=Odottaa issues.review.pending=Odottaa
issues.review.show_resolved=Näytä ratkaisu issues.review.show_resolved=Näytä ratkaisu
issues.review.hide_resolved=Piilota ratkaisu issues.review.hide_resolved=Piilota ratkaisu
issues.review.commented=Kommentoi
issues.reference_issue.body=Kuvaus issues.reference_issue.body=Kuvaus
issues.content_history.deleted=poistettu issues.content_history.deleted=poistettu
issues.content_history.edited=muokattu issues.content_history.edited=muokattu

View File

@ -217,8 +217,6 @@ string.desc=Z - A
[error] [error]
occurred=Une erreur sest produite occurred=Une erreur sest produite
missing_csrf=Requête incorrecte: aucun jeton CSRF présent
invalid_csrf=Requête incorrecte : jeton CSRF invalide
not_found=La cible n'a pu être trouvée. not_found=La cible n'a pu être trouvée.
network_error=Erreur réseau network_error=Erreur réseau
@ -1704,7 +1702,6 @@ issues.dependency.add_error_dep_not_same_repo=Les deux tickets doivent être dan
issues.review.self.approval=Vous ne pouvez approuver vos propres demandes d'ajout. issues.review.self.approval=Vous ne pouvez approuver vos propres demandes d'ajout.
issues.review.self.rejection=Vous ne pouvez demander de changements sur vos propres demandes de changement. issues.review.self.rejection=Vous ne pouvez demander de changements sur vos propres demandes de changement.
issues.review.approve=a approuvé ces modifications %s. issues.review.approve=a approuvé ces modifications %s.
issues.review.comment=Commenter
issues.review.dismissed=a révoqué lévaluation de %s %s. issues.review.dismissed=a révoqué lévaluation de %s %s.
issues.review.dismissed_label=Révoquée issues.review.dismissed_label=Révoquée
issues.review.left_comment=laisser un commentaire issues.review.left_comment=laisser un commentaire
@ -1729,6 +1726,7 @@ issues.review.hide_resolved=Réduire
issues.review.resolve_conversation=Clore la conversation issues.review.resolve_conversation=Clore la conversation
issues.review.un_resolve_conversation=Rouvrir la conversation issues.review.un_resolve_conversation=Rouvrir la conversation
issues.review.resolved_by=a marqué cette conversation comme résolue. issues.review.resolved_by=a marqué cette conversation comme résolue.
issues.review.commented=Commenter
issues.assignee.error=Tous les assignés n'ont pas été ajoutés en raison d'une erreur inattendue. issues.assignee.error=Tous les assignés n'ont pas été ajoutés en raison d'une erreur inattendue.
issues.reference_issue.body=Corps issues.reference_issue.body=Corps
issues.content_history.deleted=a supprimé issues.content_history.deleted=a supprimé

View File

@ -901,13 +901,13 @@ issues.dependency.add_error_dep_issue_not_exist=Függő hibajegy nem létezik.
issues.dependency.add_error_dep_not_exist=A függőség nem létezik. issues.dependency.add_error_dep_not_exist=A függőség nem létezik.
issues.dependency.add_error_dep_exists=A függőség már létezik. issues.dependency.add_error_dep_exists=A függőség már létezik.
issues.dependency.add_error_dep_not_same_repo=Mindkét hibajegynek ugyanabban a tárolóban kell lennie. issues.dependency.add_error_dep_not_same_repo=Mindkét hibajegynek ugyanabban a tárolóban kell lennie.
issues.review.comment=Hozzászólás
issues.review.reject=%s változtatások kérése issues.review.reject=%s változtatások kérése
issues.review.pending=Függőben issues.review.pending=Függőben
issues.review.review=Értékelés issues.review.review=Értékelés
issues.review.reviewers=Véleményezők issues.review.reviewers=Véleményezők
issues.review.show_outdated=Elavultak mutatása issues.review.show_outdated=Elavultak mutatása
issues.review.hide_outdated=Elavultak elrejtése issues.review.hide_outdated=Elavultak elrejtése
issues.review.commented=Hozzászólás
issues.assignee.error=Nem minden megbízott lett hozzáadva egy nem várt hiba miatt. issues.assignee.error=Nem minden megbízott lett hozzáadva egy nem várt hiba miatt.

View File

@ -129,8 +129,6 @@ filter.public=Opinbert
[error] [error]
occurred=Villa kom upp occurred=Villa kom upp
missing_csrf=Slæm beiðni: enginn CSRF lykill
invalid_csrf=Slæm beiðni: ógildur CSRF lykill
not_found=Markmiðið fannst ekki. not_found=Markmiðið fannst ekki.
network_error=Netkerfisvilla network_error=Netkerfisvilla
@ -850,13 +848,13 @@ issues.dependency.remove_header=Fjarlægja Kröfu
issues.dependency.add_error_dep_not_exist=Krafa er ekki til. issues.dependency.add_error_dep_not_exist=Krafa er ekki til.
issues.dependency.add_error_dep_exists=Krafa er nú þegar til. issues.dependency.add_error_dep_exists=Krafa er nú þegar til.
issues.review.approve=samþykkti þessar breytingar %s issues.review.approve=samþykkti þessar breytingar %s
issues.review.comment=Senda Ummæli
issues.review.dismissed_label=Hunsað issues.review.dismissed_label=Hunsað
issues.review.left_comment=gerði ummæli issues.review.left_comment=gerði ummæli
issues.review.pending=Í bið issues.review.pending=Í bið
issues.review.outdated=Úrelt issues.review.outdated=Úrelt
issues.review.show_outdated=Sýna úrelt issues.review.show_outdated=Sýna úrelt
issues.review.hide_outdated=Fela úreld issues.review.hide_outdated=Fela úreld
issues.review.commented=Senda Ummæli
issues.reference_issue.body=Meginmál issues.reference_issue.body=Meginmál
issues.content_history.deleted=eytt issues.content_history.deleted=eytt
issues.content_history.edited=breytt issues.content_history.edited=breytt

View File

@ -135,8 +135,6 @@ filter.private=Privati
[error] [error]
occurred=Si è verificato un errore occurred=Si è verificato un errore
missing_csrf=Richiesta errata: nessun token CSRF presente
invalid_csrf=Richiesta errata: token CSRF non valido
not_found=Il bersaglio non è stato trovato. not_found=Il bersaglio non è stato trovato.
network_error=Errore di rete network_error=Errore di rete
@ -1331,7 +1329,6 @@ issues.dependency.add_error_dep_not_same_repo=Entrambi i problemi devono essere
issues.review.self.approval=Non puoi approvare la tua pull request. issues.review.self.approval=Non puoi approvare la tua pull request.
issues.review.self.rejection=Non puoi richiedere modifiche sulla tua pull request. issues.review.self.rejection=Non puoi richiedere modifiche sulla tua pull request.
issues.review.approve=hanno approvato queste modifiche %s issues.review.approve=hanno approvato queste modifiche %s
issues.review.comment=Commentare
issues.review.dismissed=recensione %s di %s respinta issues.review.dismissed=recensione %s di %s respinta
issues.review.dismissed_label=Respinta issues.review.dismissed_label=Respinta
issues.review.left_comment=lascia un commento issues.review.left_comment=lascia un commento
@ -1352,6 +1349,7 @@ issues.review.hide_resolved=Nascondi risolte
issues.review.resolve_conversation=Risolvi la conversazione issues.review.resolve_conversation=Risolvi la conversazione
issues.review.un_resolve_conversation=Segnala la conversazione come non risolta issues.review.un_resolve_conversation=Segnala la conversazione come non risolta
issues.review.resolved_by=ha contrassegnato questa conversazione come risolta issues.review.resolved_by=ha contrassegnato questa conversazione come risolta
issues.review.commented=Commentare
issues.assignee.error=Non tutte le assegnazioni sono state aggiunte a causa di un errore imprevisto. issues.assignee.error=Non tutte le assegnazioni sono state aggiunte a causa di un errore imprevisto.
issues.reference_issue.body=Corpo issues.reference_issue.body=Corpo
issues.content_history.deleted=eliminato issues.content_history.deleted=eliminato

View File

@ -218,8 +218,6 @@ string.desc=Z - A
[error] [error]
occurred=エラーが発生しました occurred=エラーが発生しました
missing_csrf=不正なリクエスト: CSRFトークンがありません
invalid_csrf=不正なリクエスト: CSRFトークンが無効です
not_found=ターゲットが見つかりませんでした。 not_found=ターゲットが見つかりませんでした。
network_error=ネットワークエラー network_error=ネットワークエラー
@ -1714,7 +1712,6 @@ issues.dependency.add_error_dep_not_same_repo=両方とも同じリポジトリ
issues.review.self.approval=自分のプルリクエストを承認することはできません。 issues.review.self.approval=自分のプルリクエストを承認することはできません。
issues.review.self.rejection=自分のプルリクエストに対して修正を要求することはできません。 issues.review.self.rejection=自分のプルリクエストに対して修正を要求することはできません。
issues.review.approve=が変更を承認 %s issues.review.approve=が変更を承認 %s
issues.review.comment=コメント
issues.review.dismissed=が %s のレビューを棄却 %s issues.review.dismissed=が %s のレビューを棄却 %s
issues.review.dismissed_label=棄却 issues.review.dismissed_label=棄却
issues.review.left_comment=がコメント issues.review.left_comment=がコメント
@ -1739,6 +1736,7 @@ issues.review.hide_resolved=解決済みを隠す
issues.review.resolve_conversation=解決済みにする issues.review.resolve_conversation=解決済みにする
issues.review.un_resolve_conversation=未解決にする issues.review.un_resolve_conversation=未解決にする
issues.review.resolved_by=がこの会話を解決済みにしました issues.review.resolved_by=がこの会話を解決済みにしました
issues.review.commented=コメント
issues.assignee.error=予期しないエラーにより、一部の担当者を追加できませんでした。 issues.assignee.error=予期しないエラーにより、一部の担当者を追加できませんでした。
issues.reference_issue.body=内容 issues.reference_issue.body=内容
issues.content_history.deleted=削除しました issues.content_history.deleted=削除しました

View File

@ -818,12 +818,12 @@ issues.dependency.add_error_dep_not_same_repo=두 이슈는 같은 레포지토
issues.review.self.approval=자신의 풀 리퀘스트를 승인할 수 없습니다. issues.review.self.approval=자신의 풀 리퀘스트를 승인할 수 없습니다.
issues.review.self.rejection=자신의 풀 리퀘스트에 대한 변경을 요청할 수 없습니다. issues.review.self.rejection=자신의 풀 리퀘스트에 대한 변경을 요청할 수 없습니다.
issues.review.approve="이 변경사항을 승인하였습니다. %s" issues.review.approve="이 변경사항을 승인하였습니다. %s"
issues.review.comment=댓글
issues.review.pending=보류 issues.review.pending=보류
issues.review.review=검토 issues.review.review=검토
issues.review.reviewers=리뷰어 issues.review.reviewers=리뷰어
issues.review.show_outdated=오래된 내역 보기 issues.review.show_outdated=오래된 내역 보기
issues.review.hide_outdated=오래된 내역 숨기기 issues.review.hide_outdated=오래된 내역 숨기기
issues.review.commented=댓글
pulls.new=새 풀 리퀘스트 pulls.new=새 풀 리퀘스트

View File

@ -187,8 +187,6 @@ string.desc=Z - A
[error] [error]
occurred=Radusies kļūda occurred=Radusies kļūda
missing_csrf=Kļūdains pieprasījums: netika iesūtīta drošības pilnvara
invalid_csrf=Kļūdains pieprasījums: iesūtīta kļūdaina drošības pilnvara
not_found=Pieprasītie dati netika atrasti. not_found=Pieprasītie dati netika atrasti.
network_error=Tīkla kļūda network_error=Tīkla kļūda
@ -1609,7 +1607,6 @@ issues.dependency.add_error_dep_not_same_repo=Abām problēmām ir jābūt no vi
issues.review.self.approval=Nevar apstiprināt savu izmaiņu pieprasījumi. issues.review.self.approval=Nevar apstiprināt savu izmaiņu pieprasījumi.
issues.review.self.rejection=Nevar pieprasīt izmaiņas savam izmaiņu pieprasījumam. issues.review.self.rejection=Nevar pieprasīt izmaiņas savam izmaiņu pieprasījumam.
issues.review.approve=apstiprināja izmaiņas %s issues.review.approve=apstiprināja izmaiņas %s
issues.review.comment=Komentēt
issues.review.dismissed=atmeta %s recenziju %s issues.review.dismissed=atmeta %s recenziju %s
issues.review.dismissed_label=Atmesta issues.review.dismissed_label=Atmesta
issues.review.left_comment=atstāja komentāru issues.review.left_comment=atstāja komentāru
@ -1634,6 +1631,7 @@ issues.review.hide_resolved=Paslēpt atrisināto
issues.review.resolve_conversation=Atrisināt sarunu issues.review.resolve_conversation=Atrisināt sarunu
issues.review.un_resolve_conversation=Atcelt sarunas atrisinājumu issues.review.un_resolve_conversation=Atcelt sarunas atrisinājumu
issues.review.resolved_by=atzīmēja sarunu kā atrisinātu issues.review.resolved_by=atzīmēja sarunu kā atrisinātu
issues.review.commented=Komentēt
issues.assignee.error=Ne visi atbildīgie tika pievienoti, jo radās neparedzēta kļūda. issues.assignee.error=Ne visi atbildīgie tika pievienoti, jo radās neparedzēta kļūda.
issues.reference_issue.body=Saturs issues.reference_issue.body=Saturs
issues.content_history.deleted=dzēsts issues.content_history.deleted=dzēsts

View File

@ -134,8 +134,6 @@ filter.private=Prive
[error] [error]
occurred=Er is een fout opgetreden occurred=Er is een fout opgetreden
missing_csrf=Foutief verzoek: geen CSRF-token aanwezig
invalid_csrf=Verkeerd verzoek: ongeldig CSRF-token
not_found=Het doel kon niet worden gevonden. not_found=Het doel kon niet worden gevonden.
network_error=Netwerk fout network_error=Netwerk fout
@ -1328,7 +1326,6 @@ issues.dependency.add_error_dep_not_same_repo=Beide kwesties moeten in dezelfde
issues.review.self.approval=Je kan je eigen pull-aanvraag niet goedkeuren. issues.review.self.approval=Je kan je eigen pull-aanvraag niet goedkeuren.
issues.review.self.rejection=Je kan geen wijzigingen aanvragen op je eigen pull-aanvraag. issues.review.self.rejection=Je kan geen wijzigingen aanvragen op je eigen pull-aanvraag.
issues.review.approve=heeft deze veranderingen %s goedgekeurd issues.review.approve=heeft deze veranderingen %s goedgekeurd
issues.review.comment=Opmerking
issues.review.dismissed=%s's beoordeling afgewezen %s issues.review.dismissed=%s's beoordeling afgewezen %s
issues.review.dismissed_label=Afgewezen issues.review.dismissed_label=Afgewezen
issues.review.left_comment=heeft een reactie achtergelaten issues.review.left_comment=heeft een reactie achtergelaten
@ -1349,6 +1346,7 @@ issues.review.hide_resolved=Verbergen afgehandeld
issues.review.resolve_conversation=Gesprek oplossen issues.review.resolve_conversation=Gesprek oplossen
issues.review.un_resolve_conversation=Gesprek niet oplossen issues.review.un_resolve_conversation=Gesprek niet oplossen
issues.review.resolved_by=markeerde dit gesprek als opgelost issues.review.resolved_by=markeerde dit gesprek als opgelost
issues.review.commented=Opmerking
issues.assignee.error=Niet alle aangewezen personen zijn toegevoegd vanwege een onverwachte fout. issues.assignee.error=Niet alle aangewezen personen zijn toegevoegd vanwege een onverwachte fout.
issues.reference_issue.body=Inhoud issues.reference_issue.body=Inhoud
issues.content_history.deleted=verwijderd issues.content_history.deleted=verwijderd

View File

@ -131,8 +131,6 @@ filter.private=Prywatne
[error] [error]
occurred=Wystąpił błąd occurred=Wystąpił błąd
missing_csrf=Błędne żądanie: brak tokenu CSRF
invalid_csrf=Błędne żądanie: nieprawidłowy token CSRF
not_found=Nie można odnaleźć celu. not_found=Nie można odnaleźć celu.
network_error=Błąd sieci network_error=Błąd sieci
@ -1220,7 +1218,6 @@ issues.dependency.add_error_dep_not_same_repo=Oba zgłoszenia muszą być w tym
issues.review.self.approval=Nie możesz zatwierdzić swojego własnego Pull Requesta. issues.review.self.approval=Nie możesz zatwierdzić swojego własnego Pull Requesta.
issues.review.self.rejection=Nie możesz zażądać zmian w swoim własnym Pull Requeście. issues.review.self.rejection=Nie możesz zażądać zmian w swoim własnym Pull Requeście.
issues.review.approve=zatwierdza te zmiany %s issues.review.approve=zatwierdza te zmiany %s
issues.review.comment=Skomentuj
issues.review.dismissed_label=Odrzucony issues.review.dismissed_label=Odrzucony
issues.review.left_comment=zostawił komentarz issues.review.left_comment=zostawił komentarz
issues.review.content.empty=Musisz pozostawić komentarz o pożądanej zmianie/zmianach. issues.review.content.empty=Musisz pozostawić komentarz o pożądanej zmianie/zmianach.
@ -1240,6 +1237,7 @@ issues.review.hide_resolved=Ukryj rozwiązane
issues.review.resolve_conversation=Rozwiąż dyskusję issues.review.resolve_conversation=Rozwiąż dyskusję
issues.review.un_resolve_conversation=Oznacz dyskusję jako nierozstrzygniętą issues.review.un_resolve_conversation=Oznacz dyskusję jako nierozstrzygniętą
issues.review.resolved_by=oznaczył(-a) tę rozmowę jako rozwiązaną issues.review.resolved_by=oznaczył(-a) tę rozmowę jako rozwiązaną
issues.review.commented=Skomentuj
issues.assignee.error=Nie udało się dodać wszystkich wybranych osób do przypisanych przez nieoczekiwany błąd. issues.assignee.error=Nie udało się dodać wszystkich wybranych osób do przypisanych przez nieoczekiwany błąd.
issues.reference_issue.body=Treść issues.reference_issue.body=Treść
issues.content_history.edited=edytowano issues.content_history.edited=edytowano

View File

@ -184,8 +184,6 @@ string.desc=Z - A
[error] [error]
occurred=Ocorreu um erro occurred=Ocorreu um erro
missing_csrf=Pedido inválido: não tem token CSRF presente
invalid_csrf=Requisição Inválida: token CSRF inválido
not_found=Não foi possível encontrar o destino. not_found=Não foi possível encontrar o destino.
network_error=Erro de rede network_error=Erro de rede
@ -1599,7 +1597,6 @@ issues.dependency.add_error_dep_not_same_repo=Ambas as issues devem estar no mes
issues.review.self.approval=Você não pode aprovar o seu próprio pull request. issues.review.self.approval=Você não pode aprovar o seu próprio pull request.
issues.review.self.rejection=Você não pode solicitar alterações em seu próprio pull request. issues.review.self.rejection=Você não pode solicitar alterações em seu próprio pull request.
issues.review.approve=aprovou estas alterações %s issues.review.approve=aprovou estas alterações %s
issues.review.comment=Comentar
issues.review.dismissed=rejeitou a revisão de %s %s issues.review.dismissed=rejeitou a revisão de %s %s
issues.review.dismissed_label=Rejeitada issues.review.dismissed_label=Rejeitada
issues.review.left_comment=deixou um comentário issues.review.left_comment=deixou um comentário
@ -1624,6 +1621,7 @@ issues.review.hide_resolved=Ocultar resolvidas
issues.review.resolve_conversation=Resolver conversa issues.review.resolve_conversation=Resolver conversa
issues.review.un_resolve_conversation=Conversa não resolvida issues.review.un_resolve_conversation=Conversa não resolvida
issues.review.resolved_by=marcou esta conversa como resolvida issues.review.resolved_by=marcou esta conversa como resolvida
issues.review.commented=Comentar
issues.assignee.error=Nem todos os responsáveis foram adicionados devido a um erro inesperado. issues.assignee.error=Nem todos os responsáveis foram adicionados devido a um erro inesperado.
issues.reference_issue.body=Conteúdo issues.reference_issue.body=Conteúdo
issues.content_history.deleted=excluído issues.content_history.deleted=excluído

View File

@ -178,6 +178,8 @@ code_search_by_git_grep=Os resultados da pesquisa no código-fonte neste momento
package_kind=Pesquisar pacotes... package_kind=Pesquisar pacotes...
project_kind=Pesquisar planeamentos... project_kind=Pesquisar planeamentos...
branch_kind=Pesquisar ramos... branch_kind=Pesquisar ramos...
tag_kind=Pesquisar etiquetas...
tag_tooltip=Pesquisar etiquetas correspondentes. Use '%' para corresponder a qualquer sequência de números.
commit_kind=Pesquisar cometimentos... commit_kind=Pesquisar cometimentos...
runner_kind=Pesquisar executores... runner_kind=Pesquisar executores...
no_results=Não foram encontrados resultados correspondentes. no_results=Não foram encontrados resultados correspondentes.
@ -220,8 +222,6 @@ string.desc=Z - A
[error] [error]
occurred=Ocorreu um erro occurred=Ocorreu um erro
report_message=Se acredita tratar-se de um erro do Gitea, procure questões relacionadas no <a href="%s">GitHub</a> ou abra uma nova questão, se necessário. report_message=Se acredita tratar-se de um erro do Gitea, procure questões relacionadas no <a href="%s">GitHub</a> ou abra uma nova questão, se necessário.
missing_csrf=Pedido inválido: não há código CSRF
invalid_csrf=Pedido inválido: código CSRF inválido
not_found=Não foi possível encontrar o destino. not_found=Não foi possível encontrar o destino.
network_error=Erro de rede network_error=Erro de rede
@ -1731,7 +1731,7 @@ issues.dependency.add_error_dep_not_same_repo=Ambas as questões têm que estar
issues.review.self.approval=Não pode aprovar o seu próprio pedido de integração. issues.review.self.approval=Não pode aprovar o seu próprio pedido de integração.
issues.review.self.rejection=Não pode solicitar modificações sobre o seu próprio pedido de integração. issues.review.self.rejection=Não pode solicitar modificações sobre o seu próprio pedido de integração.
issues.review.approve=aprovou estas modificações %s issues.review.approve=aprovou estas modificações %s
issues.review.comment=Comentar issues.review.comment=reviu %s
issues.review.dismissed=descartou a revisão de %s %s issues.review.dismissed=descartou a revisão de %s %s
issues.review.dismissed_label=Descartada issues.review.dismissed_label=Descartada
issues.review.left_comment=deixou um comentário issues.review.left_comment=deixou um comentário
@ -1756,6 +1756,7 @@ issues.review.hide_resolved=Ocultar os concluídos
issues.review.resolve_conversation=Passar diálogo ao estado de resolvido issues.review.resolve_conversation=Passar diálogo ao estado de resolvido
issues.review.un_resolve_conversation=Passar diálogo ao estado de não resolvido issues.review.un_resolve_conversation=Passar diálogo ao estado de não resolvido
issues.review.resolved_by=marcou este diálogo como estando concluído issues.review.resolved_by=marcou este diálogo como estando concluído
issues.review.commented=Comentar
issues.review.official=Aprovada issues.review.official=Aprovada
issues.review.requested=Revisão pendente issues.review.requested=Revisão pendente
issues.review.rejected=Modificações solicitadas issues.review.rejected=Modificações solicitadas

View File

@ -182,8 +182,6 @@ string.desc=Я - А
[error] [error]
occurred=Произошла ошибка occurred=Произошла ошибка
missing_csrf=Некорректный запрос: отсутствует токен CSRF
invalid_csrf=Некорректный запрос: неверный токен CSRF
not_found=Цель не найдена. not_found=Цель не найдена.
network_error=Ошибка сети network_error=Ошибка сети
@ -1578,7 +1576,6 @@ issues.dependency.add_error_dep_not_same_repo=Обе задачи должны
issues.review.self.approval=Вы не можете одобрить собственный запрос на слияние. issues.review.self.approval=Вы не можете одобрить собственный запрос на слияние.
issues.review.self.rejection=Невозможно запрашивать изменения своего запроса на слияние. issues.review.self.rejection=Невозможно запрашивать изменения своего запроса на слияние.
issues.review.approve=одобрил(а) эти изменения %s issues.review.approve=одобрил(а) эти изменения %s
issues.review.comment=Комментировать
issues.review.dismissed=отклонен отзыв %s %s issues.review.dismissed=отклонен отзыв %s %s
issues.review.dismissed_label=Отклонено issues.review.dismissed_label=Отклонено
issues.review.left_comment=оставил комментарий issues.review.left_comment=оставил комментарий
@ -1602,6 +1599,7 @@ issues.review.hide_resolved=Скрыть разрешенные
issues.review.resolve_conversation=Покинуть диалог issues.review.resolve_conversation=Покинуть диалог
issues.review.un_resolve_conversation=Незавершённый разговор issues.review.un_resolve_conversation=Незавершённый разговор
issues.review.resolved_by=пометить этот разговор как разрешённый issues.review.resolved_by=пометить этот разговор как разрешённый
issues.review.commented=Комментировать
issues.assignee.error=Не все назначения были добавлены из-за непредвиденной ошибки. issues.assignee.error=Не все назначения были добавлены из-за непредвиденной ошибки.
issues.reference_issue.body=Тело issues.reference_issue.body=Тело
issues.content_history.deleted=удалено issues.content_history.deleted=удалено

View File

@ -118,7 +118,6 @@ filter.private=පෞද්ගලික
[filter] [filter]
[error] [error]
missing_csrf=නරක ඉල්ලීම: CSRF ටෝකන් නොමැත
[startpage] [startpage]
app_desc=වේදනාකාරී, ස්වයං-සත්කාරක Git සේවාවක් app_desc=වේදනාකාරී, ස්වයං-සත්කාරක Git සේවාවක්
@ -1200,7 +1199,6 @@ issues.dependency.add_error_dep_not_same_repo=මෙම ගැටළු දෙ
issues.review.self.approval=ඔබ ඔබේ ම අදින්න ඉල්ලීම අනුමත කළ නොහැක. issues.review.self.approval=ඔබ ඔබේ ම අදින්න ඉල්ලීම අනුමත කළ නොහැක.
issues.review.self.rejection=ඔබ ඔබේ ම අදින්න ඉල්ලීම මත වෙනස්කම් ඉල්ලා සිටිය නොහැක. issues.review.self.rejection=ඔබ ඔබේ ම අදින්න ඉල්ලීම මත වෙනස්කම් ඉල්ලා සිටිය නොහැක.
issues.review.approve=මෙම වෙනස්කම් අනුමත %s issues.review.approve=මෙම වෙනස්කම් අනුමත %s
issues.review.comment=අදහස
issues.review.dismissed=%sහි සමාලෝචනය %sප්රතික්ෂේප කරන ලද issues.review.dismissed=%sහි සමාලෝචනය %sප්රතික්ෂේප කරන ලද
issues.review.dismissed_label=බැහැර issues.review.dismissed_label=බැහැර
issues.review.left_comment=අදහසක් හැරගියා issues.review.left_comment=අදහසක් හැරගියා
@ -1221,6 +1219,7 @@ issues.review.hide_resolved=විසඳා සඟවන්න
issues.review.resolve_conversation=සංවාදය විසඳන්න issues.review.resolve_conversation=සංවාදය විසඳන්න
issues.review.un_resolve_conversation=නොවිසඳිය හැකි සංවාදය issues.review.un_resolve_conversation=නොවිසඳිය හැකි සංවාදය
issues.review.resolved_by=මෙම සංවාදය විසඳා ඇති පරිදි සලකුණු කර ඇත issues.review.resolved_by=මෙම සංවාදය විසඳා ඇති පරිදි සලකුණු කර ඇත
issues.review.commented=අදහස
issues.assignee.error=අනපේක්ෂිත දෝෂයක් හේතුවෙන් සියලුම ඇසිග්නස් එකතු නොකළේය. issues.assignee.error=අනපේක්ෂිත දෝෂයක් හේතුවෙන් සියලුම ඇසිග්නස් එකතු නොකළේය.
issues.reference_issue.body=ශරීරය issues.reference_issue.body=ශරීරය
issues.content_history.deleted=මකා දැමූ issues.content_history.deleted=මකා දැමූ

View File

@ -181,8 +181,6 @@ string.desc=Z - A
[error] [error]
occurred=Vyskytla sa chyba occurred=Vyskytla sa chyba
missing_csrf=Nesprávna žiadosť: neprítomný CSFR token
invalid_csrf=Nesprávna žiadosť: nesprávny CSFR token
not_found=Nebolo možné nájsť cieľ. not_found=Nebolo možné nájsť cieľ.
network_error=Chyba siete network_error=Chyba siete

View File

@ -1039,7 +1039,6 @@ issues.dependency.add_error_dep_not_same_repo=Båda ärendena måste vara i samm
issues.review.self.approval=Du kan inte godkänna din egen pull-begäran. issues.review.self.approval=Du kan inte godkänna din egen pull-begäran.
issues.review.self.rejection=Du kan inte begära ändringar för din egna pull-förfrågan. issues.review.self.rejection=Du kan inte begära ändringar för din egna pull-förfrågan.
issues.review.approve=godkände dessa ändringar %s issues.review.approve=godkände dessa ändringar %s
issues.review.comment=Kommentar
issues.review.left_comment=lämnade en kommentar issues.review.left_comment=lämnade en kommentar
issues.review.content.empty=Du måste skriva en kommentar som anger de önskade ändringarna. issues.review.content.empty=Du måste skriva en kommentar som anger de önskade ändringarna.
issues.review.reject=begärda ändringar %s issues.review.reject=begärda ändringar %s
@ -1056,6 +1055,7 @@ issues.review.show_resolved=Visa löst
issues.review.hide_resolved=Dölj löst issues.review.hide_resolved=Dölj löst
issues.review.resolve_conversation=Lös konversation issues.review.resolve_conversation=Lös konversation
issues.review.resolved_by=markerade denna konversation som löst issues.review.resolved_by=markerade denna konversation som löst
issues.review.commented=Kommentar
issues.assignee.error=Inte alla tilldelade har lagts till på grund av ett oväntat fel. issues.assignee.error=Inte alla tilldelade har lagts till på grund av ett oväntat fel.
issues.content_history.options=Alternativ issues.content_history.options=Alternativ

View File

@ -218,8 +218,6 @@ string.desc=Z - A
[error] [error]
occurred=Bir hata oluştu occurred=Bir hata oluştu
missing_csrf=Hatalı İstek: CSRF anahtarı yok
invalid_csrf=Hatalı İstek: geçersiz CSRF erişim anahtarı
not_found=Hedef bulunamadı. not_found=Hedef bulunamadı.
network_error=Ağ hatası network_error=Ağ hatası
@ -1704,7 +1702,6 @@ issues.dependency.add_error_dep_not_same_repo=Her iki konu da aynı depoda olmal
issues.review.self.approval=Kendi değişiklik isteğinizi onaylayamazsınız. issues.review.self.approval=Kendi değişiklik isteğinizi onaylayamazsınız.
issues.review.self.rejection=Kendi değişiklik isteğinizde değişiklik isteyemezsiniz. issues.review.self.rejection=Kendi değişiklik isteğinizde değişiklik isteyemezsiniz.
issues.review.approve=%s bu değişiklikleri onayladı issues.review.approve=%s bu değişiklikleri onayladı
issues.review.comment=Yorum Yap
issues.review.dismissed=%s incelemesini %s reddetti issues.review.dismissed=%s incelemesini %s reddetti
issues.review.dismissed_label=Reddedildi issues.review.dismissed_label=Reddedildi
issues.review.left_comment=bir yorum yaptı issues.review.left_comment=bir yorum yaptı
@ -1729,6 +1726,7 @@ issues.review.hide_resolved=Çözülenleri gizle
issues.review.resolve_conversation=Konuşmayı çöz issues.review.resolve_conversation=Konuşmayı çöz
issues.review.un_resolve_conversation=Konuşmayı çözme issues.review.un_resolve_conversation=Konuşmayı çözme
issues.review.resolved_by=bu konuşmayı çözümlenmiş olarak işaretledi issues.review.resolved_by=bu konuşmayı çözümlenmiş olarak işaretledi
issues.review.commented=Yorum Yap
issues.assignee.error=Beklenmeyen bir hata nedeniyle tüm atananlar eklenmedi. issues.assignee.error=Beklenmeyen bir hata nedeniyle tüm atananlar eklenmedi.
issues.reference_issue.body=Gövde issues.reference_issue.body=Gövde
issues.content_history.deleted=silindi issues.content_history.deleted=silindi

View File

@ -120,7 +120,6 @@ filter.private=Приватний
[error] [error]
occurred=Сталася помилка occurred=Сталася помилка
missing_csrf=Некоректний запит: токен CSRF не задано
network_error=Помилка мережі network_error=Помилка мережі
[startpage] [startpage]
@ -1245,7 +1244,6 @@ issues.dependency.add_error_dep_not_same_repo=Обидві задачі пови
issues.review.self.approval=Ви не можете схвалити власний пулл-реквест. issues.review.self.approval=Ви не можете схвалити власний пулл-реквест.
issues.review.self.rejection=Ви не можете надіслати запит на зміну на власний пулл-реквест. issues.review.self.rejection=Ви не можете надіслати запит на зміну на власний пулл-реквест.
issues.review.approve=зміни затверджено %s issues.review.approve=зміни затверджено %s
issues.review.comment=Коментар
issues.review.dismissed=відхилено відгук %s %s issues.review.dismissed=відхилено відгук %s %s
issues.review.dismissed_label=Відхилено issues.review.dismissed_label=Відхилено
issues.review.left_comment=додав коментар issues.review.left_comment=додав коментар
@ -1266,6 +1264,7 @@ issues.review.hide_resolved=Приховати вирішене
issues.review.resolve_conversation=Завершити обговорення issues.review.resolve_conversation=Завершити обговорення
issues.review.un_resolve_conversation=Поновити обговорення issues.review.un_resolve_conversation=Поновити обговорення
issues.review.resolved_by=позначив обговорення завершеним issues.review.resolved_by=позначив обговорення завершеним
issues.review.commented=Коментар
issues.assignee.error=Додано не всіх виконавців через непередбачену помилку. issues.assignee.error=Додано не всіх виконавців через непередбачену помилку.
issues.reference_issue.body=Тіло issues.reference_issue.body=Тіло
issues.content_history.deleted=видалено issues.content_history.deleted=видалено

View File

@ -217,8 +217,6 @@ string.desc=Z - A
[error] [error]
occurred=发生了一个错误 occurred=发生了一个错误
missing_csrf=错误的请求:没有 CSRF 令牌
invalid_csrf=错误的请求:无效的 CSRF 令牌
not_found=找不到目标。 not_found=找不到目标。
network_error=网络错误 network_error=网络错误
@ -1692,7 +1690,6 @@ issues.dependency.add_error_dep_not_same_repo=这两个工单必须在同一仓
issues.review.self.approval=您不能批准您自己的合并请求。 issues.review.self.approval=您不能批准您自己的合并请求。
issues.review.self.rejection=您不能请求对您自己的合并请求进行更改。 issues.review.self.rejection=您不能请求对您自己的合并请求进行更改。
issues.review.approve=于 %s 批准此合并请求 issues.review.approve=于 %s 批准此合并请求
issues.review.comment=评论
issues.review.dismissed=于 %[2]s 取消了 %[1]s 的评审 issues.review.dismissed=于 %[2]s 取消了 %[1]s 的评审
issues.review.dismissed_label=已取消 issues.review.dismissed_label=已取消
issues.review.left_comment=留下了一条评论 issues.review.left_comment=留下了一条评论
@ -1717,6 +1714,7 @@ issues.review.hide_resolved=隐藏已解决的
issues.review.resolve_conversation=已解决问题 issues.review.resolve_conversation=已解决问题
issues.review.un_resolve_conversation=未解决问题 issues.review.un_resolve_conversation=未解决问题
issues.review.resolved_by=标记问题为已解决 issues.review.resolved_by=标记问题为已解决
issues.review.commented=评论
issues.assignee.error=因为未知原因,并非所有的指派都成功。 issues.assignee.error=因为未知原因,并非所有的指派都成功。
issues.reference_issue.body=内容 issues.reference_issue.body=内容
issues.content_history.deleted=删除于 issues.content_history.deleted=删除于

View File

@ -167,8 +167,6 @@ string.desc=Z - A
[error] [error]
occurred=發生錯誤 occurred=發生錯誤
missing_csrf=錯誤的請求:未提供 CSRF token
invalid_csrf=錯誤的請求:無效的 CSRF token
not_found=找不到目標。 not_found=找不到目標。
network_error=網路錯誤 network_error=網路錯誤
@ -1467,7 +1465,6 @@ issues.dependency.add_error_dep_not_same_repo=這兩個問題必須在同一個
issues.review.self.approval=您不能核可自己的合併請求。 issues.review.self.approval=您不能核可自己的合併請求。
issues.review.self.rejection=您不能對自己的合併請求提出請求變更。 issues.review.self.rejection=您不能對自己的合併請求提出請求變更。
issues.review.approve=核可了這些變更 %s issues.review.approve=核可了這些變更 %s
issues.review.comment=留言
issues.review.dismissed=取消 %s 的審核 %s issues.review.dismissed=取消 %s 的審核 %s
issues.review.dismissed_label=已取消 issues.review.dismissed_label=已取消
issues.review.left_comment=留下了回應 issues.review.left_comment=留下了回應
@ -1488,6 +1485,7 @@ issues.review.hide_resolved=隱藏已解決
issues.review.resolve_conversation=解決對話 issues.review.resolve_conversation=解決對話
issues.review.un_resolve_conversation=取消解決對話 issues.review.un_resolve_conversation=取消解決對話
issues.review.resolved_by=標記了此對話為已解決 issues.review.resolved_by=標記了此對話為已解決
issues.review.commented=留言
issues.assignee.error=因為未預期的錯誤,未能成功加入所有負責人。 issues.assignee.error=因為未預期的錯誤,未能成功加入所有負責人。
issues.reference_issue.body=內容 issues.reference_issue.body=內容
issues.content_history.deleted=刪除 issues.content_history.deleted=刪除

View File

@ -214,6 +214,8 @@ func TagsList(ctx *context.Context) {
ctx.Data["HideBranchesInDropdown"] = true ctx.Data["HideBranchesInDropdown"] = true
ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived
namePattern := ctx.FormTrim("q")
listOptions := db.ListOptions{ listOptions := db.ListOptions{
Page: ctx.FormInt("page"), Page: ctx.FormInt("page"),
PageSize: ctx.FormInt("limit"), PageSize: ctx.FormInt("limit"),
@ -233,6 +235,7 @@ func TagsList(ctx *context.Context) {
IncludeTags: true, IncludeTags: true,
HasSha1: optional.Some(true), HasSha1: optional.Some(true),
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
NamePattern: optional.Some(namePattern),
} }
releases, err := db.Find[repo_model.Release](ctx, opts) releases, err := db.Find[repo_model.Release](ctx, opts)
@ -241,14 +244,21 @@ func TagsList(ctx *context.Context) {
return return
} }
ctx.Data["Releases"] = releases count, err := db.Count[repo_model.Release](ctx, opts)
if err != nil {
ctx.ServerError("GetReleasesByRepoID", err)
return
}
numTags := ctx.Data["NumTags"].(int64) ctx.Data["Keyword"] = namePattern
pager := context.NewPagination(int(numTags), opts.PageSize, opts.Page, 5) ctx.Data["Releases"] = releases
ctx.Data["TagCount"] = count
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases) ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases)
ctx.HTML(http.StatusOK, tplTagsList) ctx.HTML(http.StatusOK, tplTagsList)
} }

View File

@ -129,6 +129,8 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
// ensure the session uid is deleted // ensure the session uid is deleted
_ = ctx.Session.Delete("uid") _ = ctx.Session.Delete("uid")
} }
ctx.Csrf.PrepareForSessionUser(ctx)
} }
} }

View File

@ -138,10 +138,8 @@ func Contexter() func(next http.Handler) http.Handler {
csrfOpts := CsrfOptions{ csrfOpts := CsrfOptions{
Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()),
Cookie: setting.CSRFCookieName, Cookie: setting.CSRFCookieName,
SetCookie: true,
Secure: setting.SessionConfig.Secure, Secure: setting.SessionConfig.Secure,
CookieHTTPOnly: setting.CSRFCookieHTTPOnly, CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
Header: "X-Csrf-Token",
CookieDomain: setting.SessionConfig.Domain, CookieDomain: setting.SessionConfig.Domain,
CookiePath: setting.SessionConfig.CookiePath, CookiePath: setting.SessionConfig.CookiePath,
SameSite: setting.SessionConfig.SameSite, SameSite: setting.SessionConfig.SameSite,
@ -167,7 +165,7 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Base.AppendContextValue(WebContextKey, ctx) ctx.Base.AppendContextValue(WebContextKey, ctx)
ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo }) ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx) ctx.Csrf = NewCSRFProtector(csrfOpts)
// Get the last flash message from cookie // Get the last flash message from cookie
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash) lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
@ -204,8 +202,6 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["SystemConfig"] = setting.Config() ctx.Data["SystemConfig"] = setting.Config()
ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these // FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations

View File

@ -20,64 +20,42 @@
package context package context
import ( import (
"encoding/base32" "html/template"
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware" )
const (
CsrfHeaderName = "X-Csrf-Token"
CsrfFormName = "_csrf"
) )
// CSRFProtector represents a CSRF protector and is used to get the current token and validate the token. // CSRFProtector represents a CSRF protector and is used to get the current token and validate the token.
type CSRFProtector interface { type CSRFProtector interface {
// GetHeaderName returns HTTP header to search for token. // PrepareForSessionUser prepares the csrf protector for the current session user.
GetHeaderName() string PrepareForSessionUser(ctx *Context)
// GetFormName returns form value to search for token. // Validate validates the csrf token in http context.
GetFormName() string
// GetToken returns the token.
GetToken() string
// Validate validates the token in http context.
Validate(ctx *Context) Validate(ctx *Context)
// DeleteCookie deletes the cookie // DeleteCookie deletes the csrf cookie
DeleteCookie(ctx *Context) DeleteCookie(ctx *Context)
} }
type csrfProtector struct { type csrfProtector struct {
opt CsrfOptions opt CsrfOptions
// Token generated to pass via header, cookie, or hidden form value. // id must be unique per user.
Token string id string
// This value must be unique per user. // token is the valid one which wil be used by end user and passed via header, cookie, or hidden form value.
ID string token string
}
// GetHeaderName returns the name of the HTTP header for csrf token.
func (c *csrfProtector) GetHeaderName() string {
return c.opt.Header
}
// GetFormName returns the name of the form value for csrf token.
func (c *csrfProtector) GetFormName() string {
return c.opt.Form
}
// GetToken returns the current token. This is typically used
// to populate a hidden form in an HTML template.
func (c *csrfProtector) GetToken() string {
return c.Token
} }
// CsrfOptions maintains options to manage behavior of Generate. // CsrfOptions maintains options to manage behavior of Generate.
type CsrfOptions struct { type CsrfOptions struct {
// The global secret value used to generate Tokens. // The global secret value used to generate Tokens.
Secret string Secret string
// HTTP header used to set and get token.
Header string
// Form value used to set and get token.
Form string
// Cookie value used to set and get token. // Cookie value used to set and get token.
Cookie string Cookie string
// Cookie domain. // Cookie domain.
@ -87,103 +65,64 @@ type CsrfOptions struct {
CookieHTTPOnly bool CookieHTTPOnly bool
// SameSite set the cookie SameSite type // SameSite set the cookie SameSite type
SameSite http.SameSite SameSite http.SameSite
// Key used for getting the unique ID per user.
SessionKey string
// oldSessionKey saves old value corresponding to SessionKey.
oldSessionKey string
// If true, send token via X-Csrf-Token header.
SetHeader bool
// If true, send token via _csrf cookie.
SetCookie bool
// Set the Secure flag to true on the cookie. // Set the Secure flag to true on the cookie.
Secure bool Secure bool
// Disallow Origin appear in request header. // sessionKey is the key used for getting the unique ID per user.
Origin bool sessionKey string
// Cookie lifetime. Default is 0 // oldSessionKey saves old value corresponding to sessionKey.
CookieLifeTime int oldSessionKey string
} }
func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions { func newCsrfCookie(opt *CsrfOptions, value string) *http.Cookie {
if opt.Secret == "" {
randBytes, err := util.CryptoRandomBytes(8)
if err != nil {
// this panic can be handled by the recover() in http handlers
panic(fmt.Errorf("failed to generate random bytes: %w", err))
}
opt.Secret = base32.StdEncoding.EncodeToString(randBytes)
}
if opt.Header == "" {
opt.Header = "X-Csrf-Token"
}
if opt.Form == "" {
opt.Form = "_csrf"
}
if opt.Cookie == "" {
opt.Cookie = "_csrf"
}
if opt.CookiePath == "" {
opt.CookiePath = "/"
}
if opt.SessionKey == "" {
opt.SessionKey = "uid"
}
if opt.CookieLifeTime == 0 {
opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds())
}
opt.oldSessionKey = "_old_" + opt.SessionKey
return opt
}
func newCsrfCookie(c *csrfProtector, value string) *http.Cookie {
return &http.Cookie{ return &http.Cookie{
Name: c.opt.Cookie, Name: opt.Cookie,
Value: value, Value: value,
Path: c.opt.CookiePath, Path: opt.CookiePath,
Domain: c.opt.CookieDomain, Domain: opt.CookieDomain,
MaxAge: c.opt.CookieLifeTime, MaxAge: int(CsrfTokenTimeout.Seconds()),
Secure: c.opt.Secure, Secure: opt.Secure,
HttpOnly: c.opt.CookieHTTPOnly, HttpOnly: opt.CookieHTTPOnly,
SameSite: c.opt.SameSite, SameSite: opt.SameSite,
} }
} }
// PrepareCSRFProtector returns a CSRFProtector to be used for every request. func NewCSRFProtector(opt CsrfOptions) CSRFProtector {
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie. if opt.Secret == "" {
func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector { panic("CSRF secret is empty but it must be set") // it shouldn't happen because it is always set in code
opt = prepareDefaultCsrfOptions(opt)
x := &csrfProtector{opt: opt}
if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
return x
} }
opt.Cookie = util.IfZero(opt.Cookie, "_csrf")
opt.CookiePath = util.IfZero(opt.CookiePath, "/")
opt.sessionKey = "uid"
opt.oldSessionKey = "_old_" + opt.sessionKey
return &csrfProtector{opt: opt}
}
x.ID = "0" func (c *csrfProtector) PrepareForSessionUser(ctx *Context) {
uidAny := ctx.Session.Get(opt.SessionKey) c.id = "0"
if uidAny != nil { if uidAny := ctx.Session.Get(c.opt.sessionKey); uidAny != nil {
switch uidVal := uidAny.(type) { switch uidVal := uidAny.(type) {
case string: case string:
x.ID = uidVal c.id = uidVal
case int64: case int64:
x.ID = strconv.FormatInt(uidVal, 10) c.id = strconv.FormatInt(uidVal, 10)
default: default:
log.Error("invalid uid type in session: %T", uidAny) log.Error("invalid uid type in session: %T", uidAny)
} }
} }
oldUID := ctx.Session.Get(opt.oldSessionKey) oldUID := ctx.Session.Get(c.opt.oldSessionKey)
uidChanged := oldUID == nil || oldUID.(string) != x.ID uidChanged := oldUID == nil || oldUID.(string) != c.id
cookieToken := ctx.GetSiteCookie(opt.Cookie) cookieToken := ctx.GetSiteCookie(c.opt.Cookie)
needsNew := true needsNew := true
if uidChanged { if uidChanged {
_ = ctx.Session.Set(opt.oldSessionKey, x.ID) _ = ctx.Session.Set(c.opt.oldSessionKey, c.id)
} else if cookieToken != "" { } else if cookieToken != "" {
// If cookie token presents, re-use existing unexpired token, else generate a new one. // If cookie token presents, re-use existing unexpired token, else generate a new one.
if issueTime, ok := ParseCsrfToken(cookieToken); ok { if issueTime, ok := ParseCsrfToken(cookieToken); ok {
dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time. dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time.
if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval { if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval {
x.Token = cookieToken c.token = cookieToken
needsNew = false needsNew = false
} }
} }
@ -191,42 +130,33 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
if needsNew { if needsNew {
// FIXME: actionId. // FIXME: actionId.
x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now()) c.token = GenerateCsrfToken(c.opt.Secret, c.id, "POST", time.Now())
if opt.SetCookie { cookie := newCsrfCookie(&c.opt, c.token)
cookie := newCsrfCookie(x, x.Token) ctx.Resp.Header().Add("Set-Cookie", cookie.String())
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
} }
if opt.SetHeader { ctx.Data["CsrfToken"] = c.token
ctx.Resp.Header().Add(opt.Header, x.Token) ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + template.HTMLEscapeString(c.token) + `">`)
}
return x
} }
func (c *csrfProtector) validateToken(ctx *Context, token string) { func (c *csrfProtector) validateToken(ctx *Context, token string) {
if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) { if !ValidCsrfToken(token, c.opt.Secret, c.id, "POST", time.Now()) {
c.DeleteCookie(ctx) c.DeleteCookie(ctx)
if middleware.IsAPIPath(ctx.Req) { // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
// currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints. // FIXME: distinguish what the response is for: HTML (web page) or JSON (fetch)
http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest) http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
} else {
ctx.Flash.Error(ctx.Tr("error.invalid_csrf"))
ctx.Redirect(setting.AppSubURL + "/")
}
} }
} }
// Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token" // Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token"
// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated. // HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated.
// If this validation fails, custom Error is sent in the reply. // If this validation fails, http.StatusBadRequest is sent.
// If neither a header nor form value is found, http.StatusBadRequest is sent.
func (c *csrfProtector) Validate(ctx *Context) { func (c *csrfProtector) Validate(ctx *Context) {
if token := ctx.Req.Header.Get(c.GetHeaderName()); token != "" { if token := ctx.Req.Header.Get(CsrfHeaderName); token != "" {
c.validateToken(ctx, token) c.validateToken(ctx, token)
return return
} }
if token := ctx.Req.FormValue(c.GetFormName()); token != "" { if token := ctx.Req.FormValue(CsrfFormName); token != "" {
c.validateToken(ctx, token) c.validateToken(ctx, token)
return return
} }
@ -234,9 +164,7 @@ func (c *csrfProtector) Validate(ctx *Context) {
} }
func (c *csrfProtector) DeleteCookie(ctx *Context) { func (c *csrfProtector) DeleteCookie(ctx *Context) {
if c.opt.SetCookie { cookie := newCsrfCookie(&c.opt, "")
cookie := newCsrfCookie(c, "") cookie.MaxAge = -1
cookie.MaxAge = -1 ctx.Resp.Header().Add("Set-Cookie", cookie.String())
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
}
} }

View File

@ -82,43 +82,40 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
return nil return nil
} }
attachmentIDs := make([]string, 0, len(content.Attachments))
if setting.Attachment.Enabled {
for _, attachment := range content.Attachments {
a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
Name: attachment.Name,
UploaderID: doer.ID,
RepoID: issue.Repo.ID,
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
log.Info("Skipping disallowed attachment type: %s", attachment.Name)
continue
}
return err
}
attachmentIDs = append(attachmentIDs, a.UUID)
}
}
if content.Content == "" && len(attachmentIDs) == 0 {
return nil
}
switch r := ref.(type) { switch r := ref.(type) {
case *issues_model.Issue: case *issues_model.Issue:
attachmentIDs := make([]string, 0, len(content.Attachments)) _, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
if setting.Attachment.Enabled {
for _, attachment := range content.Attachments {
a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
Name: attachment.Name,
UploaderID: doer.ID,
RepoID: issue.Repo.ID,
})
if err != nil {
if upload.IsErrFileTypeForbidden(err) {
log.Info("Skipping disallowed attachment type: %s", attachment.Name)
continue
}
return err
}
attachmentIDs = append(attachmentIDs, a.UUID)
}
}
if content.Content == "" && len(attachmentIDs) == 0 {
return nil
}
_, err = issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
if err != nil { if err != nil {
return fmt.Errorf("CreateIssueComment failed: %w", err) return fmt.Errorf("CreateIssueComment failed: %w", err)
} }
case *issues_model.Comment: case *issues_model.Comment:
comment := r comment := r
if content.Content == "" { switch comment.Type {
return nil case issues_model.CommentTypeCode:
}
if comment.Type == issues_model.CommentTypeCode {
_, err := pull_service.CreateCodeComment( _, err := pull_service.CreateCodeComment(
ctx, ctx,
doer, doer,
@ -130,11 +127,16 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u
false, // not pending review but a single review false, // not pending review but a single review
comment.ReviewID, comment.ReviewID,
"", "",
nil, attachmentIDs,
) )
if err != nil { if err != nil {
return fmt.Errorf("CreateCodeComment failed: %w", err) return fmt.Errorf("CreateCodeComment failed: %w", err)
} }
default:
_, err := issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
if err != nil {
return fmt.Errorf("CreateIssueComment failed: %w", err)
}
} }
} }
return nil return nil

View File

@ -320,8 +320,9 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
} }
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{ releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
RepoID: repo.ID, RepoID: repo.ID,
TagNames: tags, TagNames: tags,
IncludeTags: true,
}) })
if err != nil { if err != nil {
return fmt.Errorf("db.Find[repo_model.Release]: %w", err) return fmt.Errorf("db.Find[repo_model.Release]: %w", err)
@ -382,12 +383,12 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
rel, has := relMap[lowerTag] rel, has := relMap[lowerTag]
parts := strings.SplitN(tag.Message, "\n", 2)
note := ""
if len(parts) > 1 {
note = parts[1]
}
if !has { if !has {
parts := strings.SplitN(tag.Message, "\n", 2)
note := ""
if len(parts) > 1 {
note = parts[1]
}
rel = &repo_model.Release{ rel = &repo_model.Release{
RepoID: repo.ID, RepoID: repo.ID,
Title: parts[0], Title: parts[0],
@ -408,10 +409,11 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
newReleases = append(newReleases, rel) newReleases = append(newReleases, rel)
} else { } else {
rel.Title = parts[0]
rel.Note = note
rel.Sha1 = commit.ID.String() rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix()) rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount rel.NumCommits = commitsCount
rel.IsDraft = false
if rel.IsTag && author != nil { if rel.IsTag && author != nil {
rel.PublisherID = author.ID rel.PublisherID = author.ID
} }

View File

@ -4,14 +4,19 @@
<div class="ui container"> <div class="ui container">
{{template "base/alert" .}} {{template "base/alert" .}}
{{template "repo/release_tag_header" .}} {{template "repo/release_tag_header" .}}
{{if .Releases}}
<h4 class="ui top attached header"> <h4 class="ui top attached header">
<div class="five wide column tw-flex tw-items-center"> <div class="five wide column tw-flex tw-items-center">
{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.tags"}} {{.TagCount}} {{ctx.Locale.Tr "repo.release.tags"}}
</div> </div>
</h4> </h4>
{{$canReadReleases := $.Permission.CanRead ctx.Consts.RepoUnitTypeReleases}} {{$canReadReleases := $.Permission.CanRead ctx.Consts.RepoUnitTypeReleases}}
<div class="ui attached segment">
<form class="ignore-dirty" method="get">
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.tag_kind") "Tooltip" (ctx.Locale.Tr "search.tag_tooltip")}}
</form>
</div>
<div class="ui attached table segment"> <div class="ui attached table segment">
{{if .Releases}}
<table class="ui very basic striped fixed table single line" id="tags-table"> <table class="ui very basic striped fixed table single line" id="tags-table">
<tbody class="tag-list"> <tbody class="tag-list">
{{range $idx, $release := .Releases}} {{range $idx, $release := .Releases}}
@ -57,9 +62,12 @@
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
{{else}}
{{if .NumTags}}
<p class="tw-p-4">{{ctx.Locale.Tr "no_results_found"}}</p>
{{end}}
{{end}}
</div> </div>
{{end}}
{{template "base/paginate" .}} {{template "base/paginate" .}}
</div> </div>
</div> </div>

View File

@ -59,7 +59,8 @@ func createAttachment(t *testing.T, session *TestSession, repoURL, filename stri
func TestCreateAnonymousAttachment(t *testing.T) { func TestCreateAnonymousAttachment(t *testing.T) {
defer tests.PrepareTestEnv(t)() defer tests.PrepareTestEnv(t)()
session := emptyTestSession(t) session := emptyTestSession(t)
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusSeeOther) // this test is not right because it just doesn't pass the CSRF validation
createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusBadRequest)
} }
func TestCreateIssueAttachment(t *testing.T) { func TestCreateIssueAttachment(t *testing.T) {

View File

@ -5,12 +5,10 @@ package integration
import ( import (
"net/http" "net/http"
"strings"
"testing" "testing"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -25,28 +23,12 @@ func TestCsrfProtection(t *testing.T) {
req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{ req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": "fake_csrf", "_csrf": "fake_csrf",
}) })
session.MakeRequest(t, req, http.StatusSeeOther) resp := session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
resp := session.MakeRequest(t, req, http.StatusSeeOther)
loc := resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t, "Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
// test web form csrf via header. TODO: should use an UI api to test // test web form csrf via header. TODO: should use an UI api to test
req = NewRequest(t, "POST", "/user/settings") req = NewRequest(t, "POST", "/user/settings")
req.Header.Add("X-Csrf-Token", "fake_csrf") req.Header.Add("X-Csrf-Token", "fake_csrf")
session.MakeRequest(t, req, http.StatusSeeOther) resp = session.MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
resp = session.MakeRequest(t, req, http.StatusSeeOther)
loc = resp.Header().Get("Location")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
assert.Equal(t, "Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
} }

View File

@ -7,6 +7,7 @@ import (
"io" "io"
"net" "net"
"net/smtp" "net/smtp"
"net/url"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -26,187 +27,190 @@ import (
) )
func TestIncomingEmail(t *testing.T) { func TestIncomingEmail(t *testing.T) {
defer tests.PrepareTestEnv(t)() onGiteaRun(t, func(t *testing.T, u *url.URL) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) t.Run("Payload", func(t *testing.T) {
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) defer tests.PrintCurrentTest(t)()
t.Run("Payload", func(t *testing.T) { comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1})
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 1}) _, err := incoming_payload.CreateReferencePayload(user)
assert.Error(t, err)
_, err := incoming_payload.CreateReferencePayload(user) issuePayload, err := incoming_payload.CreateReferencePayload(issue)
assert.Error(t, err) assert.NoError(t, err)
commentPayload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
issuePayload, err := incoming_payload.CreateReferencePayload(issue) _, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3})
assert.NoError(t, err) assert.Error(t, err)
commentPayload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
_, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, []byte{1, 2, 3}) ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload)
assert.Error(t, err) assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Issue))
assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID)
ref, err := incoming_payload.GetReferenceFromPayload(db.DefaultContext, issuePayload) ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload)
assert.NoError(t, err) assert.NoError(t, err)
assert.IsType(t, ref, new(issues_model.Issue)) assert.IsType(t, ref, new(issues_model.Comment))
assert.EqualValues(t, issue.ID, ref.(*issues_model.Issue).ID) assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID)
})
ref, err = incoming_payload.GetReferenceFromPayload(db.DefaultContext, commentPayload) t.Run("Token", func(t *testing.T) {
assert.NoError(t, err) defer tests.PrintCurrentTest(t)()
assert.IsType(t, ref, new(issues_model.Comment))
assert.EqualValues(t, comment.ID, ref.(*issues_model.Comment).ID)
})
t.Run("Token", func(t *testing.T) { payload := []byte{1, 2, 3, 4, 5}
defer tests.PrintCurrentTest(t)()
payload := []byte{1, 2, 3, 4, 5} token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
assert.NotEmpty(t, token)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload) ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token)
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, token) assert.Equal(t, token_service.ReplyHandlerType, ht)
assert.Equal(t, user.ID, u.ID)
assert.Equal(t, payload, p)
})
ht, u, p, err := token_service.ExtractToken(db.DefaultContext, token) t.Run("Handler", func(t *testing.T) {
assert.NoError(t, err) t.Run("Reply", func(t *testing.T) {
assert.Equal(t, token_service.ReplyHandlerType, ht) t.Run("Comment", func(t *testing.T) {
assert.Equal(t, user.ID, u.ID) defer tests.PrintCurrentTest(t)()
assert.Equal(t, payload, p)
})
t.Run("Handler", func(t *testing.T) { handler := &incoming.ReplyHandler{}
t.Run("Reply", func(t *testing.T) {
t.Run("Comment", func(t *testing.T) { payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload))
assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload))
content := &incoming.MailContent{
Content: "reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
})
t.Run("CodeComment", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6})
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID})
handler := &incoming.ReplyHandler{}
content := &incoming.MailContent{
Content: "code reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
payload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeCode,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment = comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
})
})
t.Run("Unsubscribe", func(t *testing.T) {
defer tests.PrintCurrentTest(t)() defer tests.PrintCurrentTest(t)()
handler := &incoming.ReplyHandler{} watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.True(t, watching)
handler := &incoming.UnsubscribeHandler{}
content := &incoming.MailContent{
Content: "unsub me",
}
payload, err := incoming_payload.CreateReferencePayload(issue) payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err) assert.NoError(t, err)
assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload))
assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload))
content := &incoming.MailContent{
Content: "reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload)) assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, comments) assert.False(t, watching)
comment := comments[len(comments)-1]
assert.Equal(t, user.ID, comment.PosterID)
assert.Equal(t, content.Content, comment.Content)
assert.NoError(t, comment.LoadAttachments(db.DefaultContext))
assert.Len(t, comment.Attachments, 1)
attachment := comment.Attachments[0]
assert.Equal(t, content.Attachments[0].Name, attachment.Name)
assert.EqualValues(t, 4, attachment.Size)
}) })
})
t.Run("CodeComment", func(t *testing.T) { if setting.IncomingEmail.Enabled {
// This test connects to the configured email server and is currently only enabled for MySql integration tests.
// It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails.
t.Run("IMAP", func(t *testing.T) {
defer tests.PrintCurrentTest(t)() defer tests.PrintCurrentTest(t)()
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6}) payload, err := incoming_payload.CreateReferencePayload(issue)
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) assert.NoError(t, err)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
handler := &incoming.ReplyHandler{}
content := &incoming.MailContent{
Content: "code reply by mail",
Attachments: []*incoming.Attachment{
{
Name: "attachment.txt",
Content: []byte("test"),
},
},
}
payload, err := incoming_payload.CreateReferencePayload(comment)
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload)) msg := gomail.NewMessage()
msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1))
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ msg.SetHeader("From", user.Email)
IssueID: issue.ID, msg.SetBody("text/plain", token)
Type: issues_model.CommentTypeCode, err = gomail.Send(&smtpTestSender{}, msg)
})
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment = comments[len(comments)-1] assert.Eventually(t, func() bool {
assert.Equal(t, user.ID, comment.PosterID) comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
assert.Equal(t, content.Content, comment.Content) IssueID: issue.ID,
assert.NoError(t, comment.LoadAttachments(db.DefaultContext)) Type: issues_model.CommentTypeComment,
assert.Empty(t, comment.Attachments) })
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
return comment.PosterID == user.ID && comment.Content == token
}, 10*time.Second, 1*time.Second)
}) })
}) }
t.Run("Unsubscribe", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
watching, err := issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.True(t, watching)
handler := &incoming.UnsubscribeHandler{}
content := &incoming.MailContent{
Content: "unsub me",
}
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload))
watching, err = issues_model.CheckIssueWatch(db.DefaultContext, user, issue)
assert.NoError(t, err)
assert.False(t, watching)
})
}) })
if setting.IncomingEmail.Enabled {
// This test connects to the configured email server and is currently only enabled for MySql integration tests.
// It sends a reply to create a comment. If the comment is not detected after 10 seconds the test fails.
t.Run("IMAP", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
payload, err := incoming_payload.CreateReferencePayload(issue)
assert.NoError(t, err)
token, err := token_service.CreateToken(token_service.ReplyHandlerType, user, payload)
assert.NoError(t, err)
msg := gomail.NewMessage()
msg.SetHeader("To", strings.Replace(setting.IncomingEmail.ReplyToAddress, setting.IncomingEmail.TokenPlaceholder, token, 1))
msg.SetHeader("From", user.Email)
msg.SetBody("text/plain", token)
err = gomail.Send(&smtpTestSender{}, msg)
assert.NoError(t, err)
assert.Eventually(t, func() bool {
comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{
IssueID: issue.ID,
Type: issues_model.CommentTypeComment,
})
assert.NoError(t, err)
assert.NotEmpty(t, comments)
comment := comments[len(comments)-1]
return comment.PosterID == user.ID && comment.Content == token
}, 10*time.Second, 1*time.Second)
})
}
} }
// A simple SMTP mail sender used for integration tests. // A simple SMTP mail sender used for integration tests.

View File

@ -17,7 +17,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/translation"
@ -146,15 +145,8 @@ func TestCreateBranchInvalidCSRF(t *testing.T) {
"_csrf": "fake_csrf", "_csrf": "fake_csrf",
"new_branch_name": "test", "new_branch_name": "test",
}) })
resp := session.MakeRequest(t, req, http.StatusSeeOther) resp := session.MakeRequest(t, req, http.StatusBadRequest)
loc := resp.Header().Get("Location") assert.Contains(t, resp.Body.String(), "Invalid CSRF token")
assert.Equal(t, setting.AppSubURL+"/", loc)
resp = session.MakeRequest(t, NewRequest(t, "GET", loc), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
assert.Equal(t,
"Bad Request: invalid CSRF token",
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
} }
func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) { func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) {

View File

@ -4,6 +4,7 @@
package integration package integration
import ( import (
"net/http"
"net/url" "net/url"
"testing" "testing"
@ -18,6 +19,7 @@ import (
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestCreateNewTagProtected(t *testing.T) { func TestCreateNewTagProtected(t *testing.T) {
@ -60,6 +62,40 @@ func TestCreateNewTagProtected(t *testing.T) {
}) })
}) })
t.Run("GitTagForce", func(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
httpContext := NewAPITestContext(t, owner.Name, repo.Name)
dstPath := t.TempDir()
u.Path = httpContext.GitPath()
u.User = url.UserPassword(owner.Name, userPassword)
doGitClone(dstPath, u)(t)
_, _, err := git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "tag", "v-1.1", "-m", "force update v2", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags").RunStdString(&git.RunOpts{Dir: dstPath})
require.Error(t, err)
assert.Contains(t, err.Error(), "the tag already exists in the remote")
_, _, err = git.NewCommand(git.DefaultContext, "push", "--tags", "--force").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
req := NewRequestf(t, "GET", "/%s/releases/tag/v-1.1", repo.FullName())
resp := MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
tagsTab := htmlDoc.Find(".release-list-title")
assert.Contains(t, tagsTab.Text(), "force update v2")
})
})
// Cleanup // Cleanup
releases, err := db.Find[repo_model.Release](db.DefaultContext, repo_model.FindReleasesOptions{ releases, err := db.Find[repo_model.Release](db.DefaultContext, repo_model.FindReleasesOptions{
IncludeTags: true, IncludeTags: true,