From 274523baf43ce588e18c2f557d1b82afefbe8dd4 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 4 Oct 2022 00:20:52 +0000 Subject: [PATCH 01/60] [skip ci] Updated translations via Crowdin --- options/locale/locale_de-DE.ini | 35 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index ec2e6ba50a..35cc9a1c53 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -227,7 +227,7 @@ default_keep_email_private_popup=E-Mail-Adressen von neuen Benutzern standardmä default_allow_create_organization=Erstellen von Organisationen standardmäßig erlauben default_allow_create_organization_popup=Neuen Nutzern das Erstellen von Organisationen standardmäßig erlauben. default_enable_timetracking=Zeiterfassung standardmäßig aktivieren -default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositories aktivieren. +default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositorys aktivieren. no_reply_address=Versteckte E-Mail-Domain no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist. password_algorithm=Passwort Hashing Algorithmus @@ -237,15 +237,15 @@ password_algorithm_helper=Lege den Passwort Hashing Algorithmus fest. Unterschie uname_holder=E-Mail-Adresse oder Benutzername password_holder=Passwort switch_dashboard_context=Kontext der Übersichtsseite wechseln -my_repos=Repositories -show_more_repos=Zeige mehr Repositories… -collaborative_repos=Gemeinschaftliche Repositories +my_repos=Repositorys +show_more_repos=Zeige mehr Repositorys… +collaborative_repos=Gemeinschaftliche Repositorys my_orgs=Meine Organisationen my_mirrors=Meine Mirrors view_home=%s ansehen search_repos=Finde ein Repository… filter=Andere Filter -filter_by_team_repositories=Nach Team Repositories filtern +filter_by_team_repositories=Nach Team-Repositorys filtern feed_of=Feed von "%s" show_archived=Archiviert @@ -258,10 +258,10 @@ show_both_private_public=Öffentliche und private anzeigen show_only_private=Nur private anzeigen show_only_public=Nur öffentliche anzeigen -issues.in_your_repos=Eigene Repositories +issues.in_your_repos=Eigene Repositorys [explore] -repos=Repositories +repos=Repositorys users=Benutzer organizations=Organisationen search=Suche @@ -269,12 +269,14 @@ code=Code search.fuzzy=Ähnlich search.match=Genau code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator. -repo_no_results=Keine passenden Repositories gefunden. +repo_no_results=Keine passenden Repositorys gefunden. user_no_results=Keine passenden Benutzer gefunden. org_no_results=Keine passenden Organisationen gefunden. code_no_results=Es konnte kein passender Code für deinen Suchbegriff gefunden werden. code_search_results=Suchergebnisse für „%s“ code_last_indexed_at=Zuletzt indexiert %s +relevant_repositories_tooltip=Repositorys, die Forks sind oder die kein Thema, kein Symbol und keine Beschreibung haben, werden ausgeblendet. +relevant_repositories=Es werden nur relevante Repositorys angezeigt, zeigt ungefilterte Ergebnisse an. [auth] @@ -337,7 +339,7 @@ email_domain_blacklisted=Du kannst dich nicht mit deiner E-Mail-Adresse registri authorize_application=Anwendung autorisieren authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst. authorize_application_created_by=Diese Anwendung wurde von %s erstellt. -authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositories und Organisationen. +authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositorys und Organisationen. authorize_title="%s" den Zugriff auf deinen Account gestatten? authorization_failed=Autorisierung fehlgeschlagen authorization_failed_desc=Die Autorisierung ist fehlgeschlagen, da wir eine ungültige Anfrage festgestellt haben. Bitte kontaktiere den Betreiber der Anwendung, die du gerade autorisieren wolltest. @@ -453,7 +455,7 @@ lang_select_error=Wähle eine Sprache aus der Liste aus. username_been_taken=Der Benutzername ist bereits vergeben. username_change_not_local_user=Nicht-lokale Benutzer dürfen ihren Nutzernamen nicht ändern. repo_name_been_taken=Der Repository-Name wird schon verwendet. -repository_force_private=Privat erzwingen ist aktiviert: Private Repositories können nicht veröffentlicht werden. +repository_force_private=Privat erzwingen ist aktiviert: Private Repositorys können nicht veröffentlicht werden. repository_files_already_exist=Dateien für dieses Repository sind bereits vorhanden. Kontaktiere den Systemadministrator. repository_files_already_exist.adopt=Dateien für dieses Repository existieren bereits und können nur übernommen werden. repository_files_already_exist.delete=Dateien für dieses Repository sind bereits vorhanden. Du must sie löschen. @@ -487,7 +489,7 @@ invalid_ssh_principal=Ungültige Identität: %s unable_verify_ssh_key=Dein SSH-Key kann nicht überprüft werden, probiere es erneut. auth_failed=Authentifizierung fehlgeschlagen: %v -still_own_repo=Dein Konto besitzt ein oder mehrere Repositories. Diese müssen zuerst gelöscht oder übertragen werden. +still_own_repo=Dein Konto besitzt ein oder mehrere Repositorys. Diese müssen zuerst gelöscht oder übertragen werden. still_has_org=Dein Account ist Mitglied in mindestens einer Organisation. Bitte verlasse diese zuerst. still_own_packages=Ihr Konto besitzt ein oder mehrere Pakete; löschen Sie diese zuerst. org_still_own_repo=Diese Organisation besitzt noch mindestens ein Repository. Bitte lösche oder übertrage diese zuerst. @@ -498,7 +500,7 @@ target_branch_not_exist=Der Ziel-Branch existiert nicht. [user] change_avatar=Profilbild ändern… join_on=Beigetreten am -repositories=Repositories +repositories=Repositorys activity=Öffentliche Aktivität followers=Follower starred=Favoriten @@ -970,7 +972,7 @@ migrate.gogs.description=Daten von notabug.org oder anderen Gogs Instanzen migri migrate.onedev.description=Daten von code.onedev.io oder anderen OneDev Instanzen migrieren. migrate.codebase.description=Daten von codebasehq.com migrieren. migrate.gitbucket.description=Daten von GitBucket Instanzen migrieren. -migrate.migrating_git=Git Daten werden migriert +migrate.migrating_git=Git-Daten werden migriert migrate.migrating_topics=Themen werden migriert migrate.migrating_milestones=Meilensteine werden migriert migrate.migrating_labels=Labels werden migriert @@ -2335,7 +2337,7 @@ settings.delete_prompt=Die Organisation wird dauerhaft gelöscht. Dies K settings.confirm_delete_account=Löschen bestätigen settings.delete_org_title=Organisation löschen settings.delete_org_desc=Diese Organisation wird dauerhaft gelöscht. Fortfahren? -settings.hooks_desc=Webhooks hinzufügen, die für alle Repositories dieser Organisation ausgelöst werden. +settings.hooks_desc=Webhooks hinzufügen, die für alle Repositorys dieser Organisation ausgelöst werden. settings.labels_desc=Labels hinzufügen, die für alle Repositories dieser Organisation genutzt werden können. @@ -2439,7 +2441,7 @@ dashboard.cron.error=Fehler in Cron: %s: %[3]s dashboard.cron.finished=Cron: %[1]s ist beendet dashboard.delete_inactive_accounts=Alle nicht aktivierten Konten löschen dashboard.delete_inactive_accounts.started=Löschen aller nicht aktivierten Account-Aufgabe gestartet. -dashboard.delete_repo_archives=Lösche alle Repository Archive (ZIP, TAR.GZ, …) +dashboard.delete_repo_archives=Lösche alle Repository-Archive (ZIP, TAR.GZ, …) dashboard.delete_repo_archives.started=Löschen aller Repository-Archive gestartet. dashboard.delete_missing_repos=Alle Repository-Datensätze mit verloren gegangenen Git-Dateien löschen dashboard.delete_missing_repos.started=Alle Repositories löschen, die die Git-Dateien-Aufgabe nicht gestartet haben. @@ -2528,8 +2530,9 @@ users.allow_create_organization=Darf Organisationen erstellen users.update_profile=Benutzerkonto aktualisieren users.delete_account=Benutzerkonto löschen users.cannot_delete_self=Du kannst dich nicht selbst löschen -users.still_own_repo=Dieser Benutzer besitzt noch mindestens ein Repository. Bitte lösche oder übertrage diese zuerst. +users.still_own_repo=Dieser Benutzer besitzt noch mindestens ein Repository. Bitte lösche oder übertrage diese(s) zuerst. users.still_has_org=Dieser Nutzer ist Mitglied einer Organisation. Du musst ihn zuerst aus allen Organisationen entfernen. +users.purge_help=Erzwinge das Löschen des Benutzers inklusive aller seiner Repositorys, Organisationen, Pakete und Kommentare. users.still_own_packages=Dieser Benutzer besitzt noch ein oder mehrere Pakete. Lösche diese Pakete zuerst. users.deletion_success=Der Account wurde gelöscht. users.reset_2fa=2FA zurücksetzen From 2d2cf589f72241c0ae3c8c05f899950eebb286a6 Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Tue, 4 Oct 2022 07:51:07 -0500 Subject: [PATCH 02/60] Fix linked account translation (#21331) fix key used for translation --- templates/user/settings/security/accountlinks.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/user/settings/security/accountlinks.tmpl b/templates/user/settings/security/accountlinks.tmpl index b726245724..e8eed63294 100644 --- a/templates/user/settings/security/accountlinks.tmpl +++ b/templates/user/settings/security/accountlinks.tmpl @@ -33,7 +33,7 @@
{{$provider}} - {{if $loginSource.IsActive}}{{$.locale.Tr "settings.active"}}{{end}} + {{if $loginSource.IsActive}}{{$.locale.Tr "repo.settings.active"}}{{end}}
{{end}} From 93df41f506fb0c3ca897def5d62abaedc93323d8 Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 5 Oct 2022 19:55:36 +0100 Subject: [PATCH 03/60] Fix slight bug in katex (#21171) There is a small bug in #20571 whereby `$a a$b b$` will not be correctly detected as a math inline block of `a a$b b`. This PR fixes this. Also reenable test cases as per #21340 Signed-off-by: Andrew Thornton Co-authored-by: wxiaoguang --- modules/markup/html_test.go | 4 +- modules/markup/markdown/markdown_test.go | 50 ++++++++++ modules/markup/markdown/math/inline_parser.go | 47 ++++++--- modules/markup/markdown/meta.go | 4 +- modules/markup/markdown/meta_test.go | 4 +- modules/markup/markdown/renderconfig.go | 96 ++++++++++--------- modules/markup/markdown/renderconfig_test.go | 17 ++-- 7 files changed, 153 insertions(+), 69 deletions(-) diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 370bc21822..e57187a677 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -7,6 +7,7 @@ package markup_test import ( "context" "io" + "os" "strings" "testing" @@ -32,6 +33,7 @@ func TestMain(m *testing.M) { if err := git.InitSimple(context.Background()); err != nil { log.Fatal("git init failed, err: %v", err) } + os.Exit(m.Run()) } func TestRender_Commits(t *testing.T) { @@ -336,7 +338,7 @@ func TestRender_emoji(t *testing.T) { `

Some text with 😄😄 2 emoji next to each other

`) test( "😎🤪🔐🤑❓", - `

😎🤪🔐🤑

`) + `

😎🤪🔐🤑

`) // should match nothing test( diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index fdbc291c94..49ed3d75d6 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -6,6 +6,7 @@ package markdown_test import ( "context" + "os" "strings" "testing" @@ -37,6 +38,7 @@ func TestMain(m *testing.M) { if err := git.InitSimple(context.Background()); err != nil { log.Fatal("git init failed, err: %v", err) } + os.Exit(m.Run()) } func TestRender_StandardLinks(t *testing.T) { @@ -426,3 +428,51 @@ func TestRenderEmojiInLinks_Issue12331(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expected, res) } + +func TestMathBlock(t *testing.T) { + const nl = "\n" + testcases := []struct { + testcase string + expected string + }{ + { + "$a$", + `

a

` + nl, + }, + { + "$ a $", + `

a

` + nl, + }, + { + "$a$ $b$", + `

a b

` + nl, + }, + { + `\(a\) \(b\)`, + `

a b

` + nl, + }, + { + `$a a$b b$`, + `

a a$b b

` + nl, + }, + { + `a a$b b`, + `

a a$b b

` + nl, + }, + { + `a$b $a a$b b$`, + `

a$b a a$b b

` + nl, + }, + { + "$$a$$", + `
a
` + nl, + }, + } + + for _, test := range testcases { + res, err := RenderString(&markup.RenderContext{}, test.testcase) + assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) + assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase) + + } +} diff --git a/modules/markup/markdown/math/inline_parser.go b/modules/markup/markdown/math/inline_parser.go index 0339674b6c..8dc88eb858 100644 --- a/modules/markup/markdown/math/inline_parser.go +++ b/modules/markup/markdown/math/inline_parser.go @@ -37,7 +37,7 @@ func NewInlineBracketParser() parser.InlineParser { return defaultInlineBracketParser } -// Trigger triggers this parser on $ +// Trigger triggers this parser on $ or \ func (parser *inlineParser) Trigger() []byte { return parser.start[0:1] } @@ -50,29 +50,50 @@ func isAlphanumeric(b byte) bool { // Parse parses the current line and returns a result of parsing. func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node { line, _ := block.PeekLine() - opener := bytes.Index(line, parser.start) - if opener < 0 { - return nil - } - if opener != 0 && isAlphanumeric(line[opener-1]) { + + if !bytes.HasPrefix(line, parser.start) { + // We'll catch this one on the next time round return nil } - opener += len(parser.start) - ender := bytes.Index(line[opener:], parser.end) - if ender < 0 { + precedingCharacter := block.PrecendingCharacter() + if precedingCharacter < 256 && isAlphanumeric(byte(precedingCharacter)) { + // need to exclude things like `a$` from being considered a start return nil } - if len(line) > opener+ender+len(parser.end) && isAlphanumeric(line[opener+ender+len(parser.end)]) { - return nil + + // move the opener marker point at the start of the text + opener := len(parser.start) + + // Now look for an ending line + ender := opener + for { + pos := bytes.Index(line[ender:], parser.end) + if pos < 0 { + return nil + } + + ender += pos + + // Now we want to check the character at the end of our parser section + // that is ender + len(parser.end) + pos = ender + len(parser.end) + if len(line) <= pos { + break + } + if !isAlphanumeric(line[pos]) { + break + } + // move the pointer onwards + ender += len(parser.end) } block.Advance(opener) _, pos := block.Position() node := NewInline() - segment := pos.WithStop(pos.Start + ender) + segment := pos.WithStop(pos.Start + ender - opener) node.AppendChild(node, ast.NewRawTextSegment(segment)) - block.Advance(ender + len(parser.end)) + block.Advance(ender - opener + len(parser.end)) trimBlock(node, block) return node diff --git a/modules/markup/markdown/meta.go b/modules/markup/markdown/meta.go index b08121e868..45d79d537a 100644 --- a/modules/markup/markdown/meta.go +++ b/modules/markup/markdown/meta.go @@ -88,7 +88,9 @@ func ExtractMetadataBytes(contents []byte, out interface{}) ([]byte, error) { line := contents[start:end] if isYAMLSeparator(line) { front = contents[frontMatterStart:start] - body = contents[end+1:] + if end+1 < len(contents) { + body = contents[end+1:] + } break } } diff --git a/modules/markup/markdown/meta_test.go b/modules/markup/markdown/meta_test.go index 9332b35b42..720d0066f4 100644 --- a/modules/markup/markdown/meta_test.go +++ b/modules/markup/markdown/meta_test.go @@ -61,7 +61,7 @@ func TestExtractMetadataBytes(t *testing.T) { var meta structs.IssueTemplate body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s\n%s", sepTest, frontTest, sepTest, bodyTest)), &meta) assert.NoError(t, err) - assert.Equal(t, bodyTest, body) + assert.Equal(t, bodyTest, string(body)) assert.Equal(t, metaTest, meta) assert.True(t, validateMetadata(meta)) }) @@ -82,7 +82,7 @@ func TestExtractMetadataBytes(t *testing.T) { var meta structs.IssueTemplate body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest)), &meta) assert.NoError(t, err) - assert.Equal(t, "", body) + assert.Equal(t, "", string(body)) assert.Equal(t, metaTest, meta) assert.True(t, validateMetadata(meta)) }) diff --git a/modules/markup/markdown/renderconfig.go b/modules/markup/markdown/renderconfig.go index 003579115f..1ba75dbb68 100644 --- a/modules/markup/markdown/renderconfig.go +++ b/modules/markup/markdown/renderconfig.go @@ -5,10 +5,9 @@ package markdown import ( + "fmt" "strings" - "code.gitea.io/gitea/modules/log" - "github.com/yuin/goldmark/ast" "gopkg.in/yaml.v3" ) @@ -33,17 +32,13 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error { } rc.yamlNode = value - type basicRenderConfig struct { - Gitea *yaml.Node `yaml:"gitea"` - TOC bool `yaml:"include_toc"` - Lang string `yaml:"lang"` + type commonRenderConfig struct { + TOC bool `yaml:"include_toc"` + Lang string `yaml:"lang"` } - - var basic basicRenderConfig - - err := value.Decode(&basic) - if err != nil { - return err + var basic commonRenderConfig + if err := value.Decode(&basic); err != nil { + return fmt.Errorf("unable to decode into commonRenderConfig %w", err) } if basic.Lang != "" { @@ -51,14 +46,48 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error { } rc.TOC = basic.TOC - if basic.Gitea == nil { + + type controlStringRenderConfig struct { + Gitea string `yaml:"gitea"` + } + + var stringBasic controlStringRenderConfig + + if err := value.Decode(&stringBasic); err == nil { + if stringBasic.Gitea != "" { + switch strings.TrimSpace(strings.ToLower(stringBasic.Gitea)) { + case "none": + rc.Meta = "none" + case "table": + rc.Meta = "table" + default: // "details" + rc.Meta = "details" + } + } return nil } - var control *string - if err := basic.Gitea.Decode(&control); err == nil && control != nil { - log.Info("control %v", control) - switch strings.TrimSpace(strings.ToLower(*control)) { + type giteaControl struct { + Meta *string `yaml:"meta"` + Icon *string `yaml:"details_icon"` + TOC *bool `yaml:"include_toc"` + Lang *string `yaml:"lang"` + } + + type complexGiteaConfig struct { + Gitea *giteaControl `yaml:"gitea"` + } + var complex complexGiteaConfig + if err := value.Decode(&complex); err != nil { + return fmt.Errorf("unable to decode into complexRenderConfig %w", err) + } + + if complex.Gitea == nil { + return nil + } + + if complex.Gitea.Meta != nil { + switch strings.TrimSpace(strings.ToLower(*complex.Gitea.Meta)) { case "none": rc.Meta = "none" case "table": @@ -66,39 +95,18 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error { default: // "details" rc.Meta = "details" } - return nil } - type giteaControl struct { - Meta string `yaml:"meta"` - Icon string `yaml:"details_icon"` - TOC *yaml.Node `yaml:"include_toc"` - Lang string `yaml:"lang"` + if complex.Gitea.Icon != nil { + rc.Icon = strings.TrimSpace(strings.ToLower(*complex.Gitea.Icon)) } - var controlStruct *giteaControl - if err := basic.Gitea.Decode(controlStruct); err != nil || controlStruct == nil { - return err + if complex.Gitea.Lang != nil && *complex.Gitea.Lang != "" { + rc.Lang = *complex.Gitea.Lang } - switch strings.TrimSpace(strings.ToLower(controlStruct.Meta)) { - case "none": - rc.Meta = "none" - case "table": - rc.Meta = "table" - default: // "details" - rc.Meta = "details" - } - - rc.Icon = strings.TrimSpace(strings.ToLower(controlStruct.Icon)) - - if controlStruct.Lang != "" { - rc.Lang = controlStruct.Lang - } - - var toc bool - if err := controlStruct.TOC.Decode(&toc); err == nil { - rc.TOC = toc + if complex.Gitea.TOC != nil { + rc.TOC = *complex.Gitea.TOC } return nil diff --git a/modules/markup/markdown/renderconfig_test.go b/modules/markup/markdown/renderconfig_test.go index 1027035cda..672edbf46d 100644 --- a/modules/markup/markdown/renderconfig_test.go +++ b/modules/markup/markdown/renderconfig_test.go @@ -5,6 +5,7 @@ package markdown import ( + "strings" "testing" "gopkg.in/yaml.v3" @@ -81,9 +82,9 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) { TOC: true, Lang: "testlang", }, ` - include_toc: true - lang: testlang -`, + include_toc: true + lang: testlang + `, }, { "complexlang", &RenderConfig{ @@ -91,9 +92,9 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) { Icon: "table", Lang: "testlang", }, ` - gitea: - lang: testlang -`, + gitea: + lang: testlang + `, }, { "complexlang2", &RenderConfig{ @@ -140,8 +141,8 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) { Icon: "table", Lang: "", } - if err := yaml.Unmarshal([]byte(tt.args), got); err != nil { - t.Errorf("RenderConfig.UnmarshalYAML() error = %v", err) + if err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", " ")), got); err != nil { + t.Errorf("RenderConfig.UnmarshalYAML() error = %v\n%q", err, tt.args) return } From f693863a4fcf346c13a6262ea8aae8b335ebebd3 Mon Sep 17 00:00:00 2001 From: rock2dust Date: Thu, 6 Oct 2022 03:24:21 +0800 Subject: [PATCH 04/60] Remove extra space from title element (#21345) Removes a spurious additional space in the head template. ![](https://user-images.githubusercontent.com/76462613/194077336-1e757242-6f92-4238-8856-746b2e9a1a2c.png) --- templates/base/head.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 39749613de..50253038ed 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -3,7 +3,7 @@ - {{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}} + {{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}} From abd59cd5cdd94dae844d3fcf7bc89e9d455b363c Mon Sep 17 00:00:00 2001 From: rock2dust Date: Thu, 6 Oct 2022 03:38:30 +0800 Subject: [PATCH 05/60] Update comment about repository.DISABLED_REPO_UNITS in app.example.ini (#21343) Add allowed values: `repo.projects` --- custom/conf/app.example.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 3759428ed5..ec0b7c5235 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -887,7 +887,7 @@ ROUTER = console ;USE_COMPAT_SSH_URI = false ;; ;; Close issues as long as a commit on any branch marks it as fixed -;; Comma separated list of globally disabled repo units. Allowed values: repo.issues, repo.ext_issues, repo.pulls, repo.wiki, repo.ext_wiki +;; Comma separated list of globally disabled repo units. Allowed values: repo.issues, repo.ext_issues, repo.pulls, repo.wiki, repo.ext_wiki, repo.projects ;DISABLED_REPO_UNITS = ;; ;; Comma separated list of default repo units. Allowed values: repo.code, repo.releases, repo.issues, repo.pulls, repo.wiki, repo.projects. From 8765f139c7a1f3b5aafb83ae9b095e6066d77a50 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 6 Oct 2022 04:26:34 +0800 Subject: [PATCH 06/60] Fix typo in API comment document (#21347) Close #21307 After the fix: ![image](https://user-images.githubusercontent.com/2114189/194120843-52566b84-6e29-4f91-859a-eb5839c68c54.png) --- modules/structs/org_team.go | 6 +++--- templates/swagger/v1_json.tmpl | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/modules/structs/org_team.go b/modules/structs/org_team.go index 53e3fcf62d..10bd9e62ce 100644 --- a/modules/structs/org_team.go +++ b/modules/structs/org_team.go @@ -17,7 +17,7 @@ type Team struct { // example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"] // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` - // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"] + // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"} UnitsMap map[string]string `json:"units_map"` CanCreateOrgRepo bool `json:"can_create_org_repo"` } @@ -33,7 +33,7 @@ type CreateTeamOption struct { // example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"] // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` - // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"] + // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"} UnitsMap map[string]string `json:"units_map"` CanCreateOrgRepo bool `json:"can_create_org_repo"` } @@ -49,7 +49,7 @@ type EditTeamOption struct { // example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"] // Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions. Units []string `json:"units"` - // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"] + // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"} UnitsMap map[string]string `json:"units_map"` CanCreateOrgRepo *bool `json:"can_create_org_repo"` } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index b0e94b8bb5..00d769cdd1 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -15030,7 +15030,16 @@ "type": "string" }, "x-go-name": "UnitsMap", - "example": "{\"repo.code\":\"read\",\"repo.issues\":\"write\",\"repo.ext_issues\":\"none\",\"repo.wiki\":\"admin\",\"repo.pulls\":\"owner\",\"repo.releases\":\"none\",\"repo.projects\":\"none\",\"repo.ext_wiki\":\"none\"]" + "example": { + "repo.code": "read", + "repo.ext_issues": "none", + "repo.ext_wiki": "none", + "repo.issues": "write", + "repo.projects": "none", + "repo.pulls": "owner", + "repo.releases": "none", + "repo.wiki": "admin" + } } }, "x-go-package": "code.gitea.io/gitea/modules/structs" @@ -15871,7 +15880,16 @@ "type": "string" }, "x-go-name": "UnitsMap", - "example": "{\"repo.code\":\"read\",\"repo.issues\":\"write\",\"repo.ext_issues\":\"none\",\"repo.wiki\":\"admin\",\"repo.pulls\":\"owner\",\"repo.releases\":\"none\",\"repo.projects\":\"none\",\"repo.ext_wiki\":\"none\"]" + "example": { + "repo.code": "read", + "repo.ext_issues": "none", + "repo.ext_wiki": "none", + "repo.issues": "write", + "repo.projects": "none", + "repo.pulls": "owner", + "repo.releases": "none", + "repo.wiki": "admin" + } } }, "x-go-package": "code.gitea.io/gitea/modules/structs" @@ -18582,7 +18600,16 @@ "type": "string" }, "x-go-name": "UnitsMap", - "example": "{\"repo.code\":\"read\",\"repo.issues\":\"write\",\"repo.ext_issues\":\"none\",\"repo.wiki\":\"admin\",\"repo.pulls\":\"owner\",\"repo.releases\":\"none\",\"repo.projects\":\"none\",\"repo.ext_wiki\":\"none\"]" + "example": { + "repo.code": "read", + "repo.ext_issues": "none", + "repo.ext_wiki": "none", + "repo.issues": "write", + "repo.projects": "none", + "repo.pulls": "owner", + "repo.releases": "none", + "repo.wiki": "admin" + } } }, "x-go-package": "code.gitea.io/gitea/modules/structs" From fd2d5f06b087965ee588f8e74853cd2032130efa Mon Sep 17 00:00:00 2001 From: Gennady Kovshenin Date: Thu, 6 Oct 2022 06:21:04 +0300 Subject: [PATCH 07/60] Add `stat` to `ToCommit` function for speed (#21337) Calls to ToCommit are very slow due to fetching diffs, analyzing files. This patch lets us supply `stat` as false to speed fetching a commit when we don't need the diff. /v1/repo/commits has a default `stat` set as true now. Set to false to experience fetching thousands of commits per second instead of 2-5 per second. --- modules/convert/git_commit.go | 59 ++++++++++++++++++---------------- routers/api/v1/repo/commits.go | 11 +++++-- routers/api/v1/repo/notes.go | 2 +- routers/api/v1/repo/pull.go | 2 +- templates/swagger/v1_json.tmpl | 6 ++++ 5 files changed, 49 insertions(+), 31 deletions(-) diff --git a/modules/convert/git_commit.go b/modules/convert/git_commit.go index dfd6cb080c..6015a73712 100644 --- a/modules/convert/git_commit.go +++ b/modules/convert/git_commit.go @@ -73,7 +73,7 @@ func ToPayloadCommit(repo *repo_model.Repository, c *git.Commit) *api.PayloadCom } // ToCommit convert a git.Commit to api.Commit -func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, userCache map[string]*user_model.User) (*api.Commit, error) { +func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, userCache map[string]*user_model.User, stat bool) (*api.Commit, error) { var apiAuthor, apiCommitter *api.User // Retrieve author and committer information @@ -133,28 +133,7 @@ func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git. } } - // Retrieve files affected by the commit - fileStatus, err := git.GetCommitFileStatus(gitRepo.Ctx, repo.RepoPath(), commit.ID.String()) - if err != nil { - return nil, err - } - affectedFileList := make([]*api.CommitAffectedFiles, 0, len(fileStatus.Added)+len(fileStatus.Removed)+len(fileStatus.Modified)) - for _, files := range [][]string{fileStatus.Added, fileStatus.Removed, fileStatus.Modified} { - for _, filename := range files { - affectedFileList = append(affectedFileList, &api.CommitAffectedFiles{ - Filename: filename, - }) - } - } - - diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{ - AfterCommitID: commit.ID.String(), - }) - if err != nil { - return nil, err - } - - return &api.Commit{ + res := &api.Commit{ CommitMeta: &api.CommitMeta{ URL: repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()), SHA: commit.ID.String(), @@ -188,11 +167,37 @@ func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git. Author: apiAuthor, Committer: apiCommitter, Parents: apiParents, - Files: affectedFileList, - Stats: &api.CommitStats{ + } + + // Retrieve files affected by the commit + if stat { + fileStatus, err := git.GetCommitFileStatus(gitRepo.Ctx, repo.RepoPath(), commit.ID.String()) + if err != nil { + return nil, err + } + affectedFileList := make([]*api.CommitAffectedFiles, 0, len(fileStatus.Added)+len(fileStatus.Removed)+len(fileStatus.Modified)) + for _, files := range [][]string{fileStatus.Added, fileStatus.Removed, fileStatus.Modified} { + for _, filename := range files { + affectedFileList = append(affectedFileList, &api.CommitAffectedFiles{ + Filename: filename, + }) + } + } + + diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{ + AfterCommitID: commit.ID.String(), + }) + if err != nil { + return nil, err + } + + res.Files = affectedFileList + res.Stats = &api.CommitStats{ Total: diff.TotalAddition + diff.TotalDeletion, Additions: diff.TotalAddition, Deletions: diff.TotalDeletion, - }, - }, nil + } + } + + return res, nil } diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 12c329c203..4e77c3f2f5 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -70,7 +70,7 @@ func getCommit(ctx *context.APIContext, identifier string) { return } - json, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil) + json, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, true) if err != nil { ctx.Error(http.StatusInternalServerError, "toCommit", err) return @@ -104,6 +104,10 @@ func GetAllCommits(ctx *context.APIContext) { // in: query // description: filepath of a file/dir // type: string + // - name: stat + // in: query + // description: include diff stats for every commit (disable for speedup, default 'true') + // type: boolean // - name: page // in: query // description: page number of results to return (1-based) @@ -209,9 +213,12 @@ func GetAllCommits(ctx *context.APIContext) { userCache := make(map[string]*user_model.User) apiCommits := make([]*api.Commit, len(commits)) + + stat := ctx.FormString("stat") == "" || ctx.FormBool("stat") + for i, commit := range commits { // Create json struct - apiCommits[i], err = convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache) + apiCommits[i], err = convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, stat) if err != nil { ctx.Error(http.StatusInternalServerError, "toCommit", err) return diff --git a/routers/api/v1/repo/notes.go b/routers/api/v1/repo/notes.go index 67f097a424..ee3133adec 100644 --- a/routers/api/v1/repo/notes.go +++ b/routers/api/v1/repo/notes.go @@ -69,7 +69,7 @@ func getNote(ctx *context.APIContext, identifier string) { return } - cmt, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, note.Commit, nil) + cmt, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, note.Commit, nil, true) if err != nil { ctx.Error(http.StatusInternalServerError, "ToCommit", err) return diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 2cf30e7c47..f6507dceba 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1306,7 +1306,7 @@ func GetPullRequestCommits(ctx *context.APIContext) { apiCommits := make([]*api.Commit, 0, end-start) for i := start; i < end; i++ { - apiCommit, err := convert.ToCommit(ctx.Repo.Repository, baseGitRepo, commits[i], userCache) + apiCommit, err := convert.ToCommit(ctx.Repo.Repository, baseGitRepo, commits[i], userCache, true) if err != nil { ctx.ServerError("toCommit", err) return diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 00d769cdd1..5ca3794710 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -3264,6 +3264,12 @@ "name": "path", "in": "query" }, + { + "type": "boolean", + "description": "include diff stats for every commit (disable for speedup, default 'true')", + "name": "stat", + "in": "query" + }, { "type": "integer", "description": "page number of results to return (1-based)", From 1294f6c511d0aca4608ba80a38f2d06f980c341a Mon Sep 17 00:00:00 2001 From: rock2dust Date: Thu, 6 Oct 2022 11:52:43 +0800 Subject: [PATCH 08/60] Fix default theme-auto selector when nologin (#21346) the bug is theme selector is `theme-` when not login to Gitea ![theme-auto](https://user-images.githubusercontent.com/76462613/194099390-0ff6854a-1eb9-4dba-bb28-fd238f2225f8.png) Co-authored-by: silverwind Co-authored-by: zeripath Co-authored-by: delvh Co-authored-by: wxiaoguang --- templates/base/head.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 50253038ed..70a88ff755 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -1,5 +1,5 @@ - + From b001812df41fcf86f17a90f9fead2c27bf544e63 Mon Sep 17 00:00:00 2001 From: delvh Date: Thu, 6 Oct 2022 08:00:54 +0200 Subject: [PATCH 09/60] Fix and improve incorrect error messages (#21342) L --- modules/options/static.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/options/static.go b/modules/options/static.go index d9a6c83664..4d60879be3 100644 --- a/modules/options/static.go +++ b/modules/options/static.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Gitea Authors. All rights reserved. +// 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. @@ -32,12 +32,12 @@ func Dir(name string) ([]string, error) { customDir := path.Join(setting.CustomPath, "options", name) isDir, err := util.IsDir(customDir) if err != nil { - return []string{}, fmt.Errorf("Failed to check if custom directory %s is a directory. %v", err) + return []string{}, fmt.Errorf("unable to check if custom directory %q is a directory. %w", customDir, err) } if isDir { files, err := util.StatDir(customDir, true) if err != nil { - return []string{}, fmt.Errorf("Failed to read custom directory. %v", err) + return []string{}, fmt.Errorf("unable to read custom directory %q. %w", customDir, err) } result = append(result, files...) @@ -45,11 +45,10 @@ func Dir(name string) ([]string, error) { files, err := AssetDir(name) if err != nil { - return []string{}, fmt.Errorf("Failed to read embedded directory. %v", err) + return []string{}, fmt.Errorf("unable to read embedded directory %q. %w", name, err) } result = append(result, files...) - return directories.AddAndGet(name, result), nil } From 2d3b52c24458df9ac0986546810e54aa36c2d196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bogus=C5=82awski?= Date: Thu, 6 Oct 2022 22:50:38 +0200 Subject: [PATCH 10/60] SessionUser protection against nil pointer dereference (#21358) `SessionUser` should be protected against passing `sess` = `nil` to avoid ``` PANIC: runtime error: invalid memory address or nil pointer dereference ``` in https://github.com/go-gitea/gitea/pull/18452/files#diff-a215b82aadeb8b4c4632fcf31215dd421f804eb1c0137ec6721b980136e4442aR69 after upgrade from gitea v1.16 to v1.17. Related: https://github.com/go-gitea/gitea/pull/18452 Author-Change-Id: IB#1126459 --- services/auth/session.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/auth/session.go b/services/auth/session.go index 6a23a17665..1ec94aa0af 100644 --- a/services/auth/session.go +++ b/services/auth/session.go @@ -39,6 +39,10 @@ func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataSto // SessionUser returns the user object corresponding to the "uid" session variable. func SessionUser(sess SessionStore) *user_model.User { + if sess == nil { + return nil + } + // Get user ID uid := sess.Get("uid") if uid == nil { From 64073276c444c875afe0176c3cf1429ce2a24716 Mon Sep 17 00:00:00 2001 From: zeripath Date: Thu, 6 Oct 2022 21:51:18 +0100 Subject: [PATCH 11/60] Update go to 1.19 (#21361) It appears that updating go to 1.19 for playwright was missed when we updated to go 1.19 elsewhere. Signed-off-by: Andrew Thornton --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 95d4032413..59cc4d8f7b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -573,7 +573,7 @@ steps: - name: test-e2e image: mcr.microsoft.com/playwright:v1.24.0-focal commands: - - curl -sLO https://go.dev/dl/go1.18.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.18.linux-amd64.tar.gz + - curl -sLO https://go.dev/dl/go1.19.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz - groupadd --gid 1001 gitea && useradd -m --gid 1001 --uid 1001 gitea - apt-get -qq update && apt-get -qqy install build-essential - export TEST_PGSQL_SCHEMA='' From f1f911df41cc7c566348cae3672a32291d98a890 Mon Sep 17 00:00:00 2001 From: Clark Boylan Date: Thu, 6 Oct 2022 13:51:38 -0700 Subject: [PATCH 12/60] Update to go-enry v2.8.3 (#21360) This fixes an issue with enry's isVendor() method being too greedy. This lead to gitea classifying unvendored code as vendored. The impact of this is fairly minimal, but our Gitea users did notice which led me to fixing this in go-enry. Some files will be tagged with a vendored flag in the UI. I think it also impacts the calculation of language statistics in the repo as vendored files are not incorporated into the stats. For more information on the issue see the go-enry bug: https://github.com/go-enry/go-enry/issues/135 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4501944a22..2ddf6f2ff3 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/go-ap/jsonld v0.0.0-20220917142617-76bf51585778 github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 - github.com/go-enry/go-enry/v2 v2.8.2 + github.com/go-enry/go-enry/v2 v2.8.3 github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e github.com/go-git/go-billy/v5 v5.3.1 github.com/go-git/go-git/v5 v5.4.3-0.20220529141257-bc1f419cebcf diff --git a/go.sum b/go.sum index b6d76dfe6f..7378dfb932 100644 --- a/go.sum +++ b/go.sum @@ -485,8 +485,8 @@ github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-enry/go-enry/v2 v2.8.2 h1:uiGmC+3K8sVd/6DOe2AOJEOihJdqda83nPyJNtMR8RI= -github.com/go-enry/go-enry/v2 v2.8.2/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ= +github.com/go-enry/go-enry/v2 v2.8.3 h1:BwvNrN58JqBJhyyVdZSl5QD3xoxEEGYUrRyPh31FGhw= +github.com/go-enry/go-enry/v2 v2.8.3/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ= github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4= github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5HqlEWMLIcDmLpIELlG4iGbd0s8iqgPi8= From d7f0d8d8eab168d9d379c7a8b8c15aa3cc046516 Mon Sep 17 00:00:00 2001 From: Joe Constant Date: Thu, 6 Oct 2022 14:53:02 -0600 Subject: [PATCH 13/60] Add redirect of /upgrade/ to /upgrade-from-gitea/ on docs site (#21330) Since adding an aliases block doesn't seem to work locally for me (I suspect because a page actually exists and Hugo is granting preference to existing pages over aliases), I also added entries to static/_redirects file so Netlify will handle the redirects Fixes #7208 --- docs/content/doc/upgrade/from-gitea.en-us.md | 2 ++ docs/static/_redirects | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/docs/content/doc/upgrade/from-gitea.en-us.md b/docs/content/doc/upgrade/from-gitea.en-us.md index 0d22a32439..4dc47b05a4 100644 --- a/docs/content/doc/upgrade/from-gitea.en-us.md +++ b/docs/content/doc/upgrade/from-gitea.en-us.md @@ -1,6 +1,8 @@ --- date: "2021-09-02T16:00:00+08:00" title: "Upgrade from an old Gitea" +aliases: + - /en-us/upgrade/ slug: "upgrade-from-gitea" weight: 10 toc: false diff --git a/docs/static/_redirects b/docs/static/_redirects index 2114ae933f..676181b61f 100644 --- a/docs/static/_redirects +++ b/docs/static/_redirects @@ -10,3 +10,7 @@ https://gitea-docs.netlify.com/* https://docs.gitea.io/:splat 302! /en-us/ci-cd/ /en-us/integrations/ 302! /en-us/third-party-tools/ /en-us/integrations/ 302! /en-us/make/ /en-us/hacking-on-gitea/ 302! +/en-us/upgrade/ /en-us/upgrade-from-gitea/ 302! +/fr-fr/upgrade/ /fr-fr/upgrade-from-gitea/ 302! +/zh-cn/upgrade/ /zh-cn/upgrade-from-gitea/ 302! +/zh-tw/upgrade/ /zh-tw/upgrade-from-gitea/ 302! From f09f73d784eb3a51da3d108f8be5116441cbd73d Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 6 Oct 2022 22:55:26 +0200 Subject: [PATCH 14/60] Disable Firefox E2E tests (#21363) Make CI green again, until we figure out https://github.com/go-gitea/gitea/issues/21355. --- playwright.config.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/playwright.config.js b/playwright.config.js index af67717a2d..d5201ca0a0 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -64,12 +64,13 @@ export default { }, }, - { - name: 'firefox', - use: { - ...devices['Desktop Firefox'], - }, - }, + // disabled because of https://github.com/go-gitea/gitea/issues/21355 + // { + // name: 'firefox', + // use: { + // ...devices['Desktop Firefox'], + // }, + // }, { name: 'webkit', From 34f509eb7a48e698114261de323834482553a98c Mon Sep 17 00:00:00 2001 From: M Hickford Date: Fri, 7 Oct 2022 03:53:49 +0100 Subject: [PATCH 15/60] Parse OAuth Authorization header when request omits client secret (#21351) This fixes error "unauthorized_client: invalid client secret" when client includes secret in Authorization header rather than request body. OAuth spec permits both. Sanity validation that client id and client secret in request are consistent with Authorization header. Improve error descriptions. Error codes remain the same. Co-authored-by: wxiaoguang Co-authored-by: zeripath --- routers/web/auth/oauth.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index d145150535..e6112b4276 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -588,7 +588,8 @@ func OIDCKeys(ctx *context.Context) { // AccessTokenOAuth manages all access token requests by the client func AccessTokenOAuth(ctx *context.Context) { form := *web.GetForm(ctx).(*forms.AccessTokenForm) - if form.ClientID == "" { + // if there is no ClientID or ClientSecret in the request body, fill these fields by the Authorization header and ensure the provided field matches the Authorization header + if form.ClientID == "" || form.ClientSecret == "" { authHeader := ctx.Req.Header.Get("Authorization") authContent := strings.SplitN(authHeader, " ", 2) if len(authContent) == 2 && authContent[0] == "Basic" { @@ -608,7 +609,21 @@ func AccessTokenOAuth(ctx *context.Context) { }) return } + if form.ClientID != "" && form.ClientID != pair[0] { + handleAccessTokenError(ctx, AccessTokenError{ + ErrorCode: AccessTokenErrorCodeInvalidRequest, + ErrorDescription: "client_id in request body inconsistent with Authorization header", + }) + return + } form.ClientID = pair[0] + if form.ClientSecret != "" && form.ClientSecret != pair[1] { + handleAccessTokenError(ctx, AccessTokenError{ + ErrorCode: AccessTokenErrorCodeInvalidRequest, + ErrorDescription: "client_secret in request body inconsistent with Authorization header", + }) + return + } form.ClientSecret = pair[1] } } @@ -686,9 +701,13 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, s return } if !app.ValidateClientSecret([]byte(form.ClientSecret)) { + errorDescription := "invalid client secret" + if form.ClientSecret == "" { + errorDescription = "invalid empty client secret" + } handleAccessTokenError(ctx, AccessTokenError{ ErrorCode: AccessTokenErrorCodeUnauthorizedClient, - ErrorDescription: "invalid client secret", + ErrorDescription: errorDescription, }) return } From 30ca91666ebd3168923baa82b4a17b039039d0a9 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Fri, 7 Oct 2022 06:22:05 +0200 Subject: [PATCH 16/60] Set SemverCompatible to false for Conan packages (#21275) Fixes #21250 Related #20414 Conan packages don't have to follow SemVer. The migration fixes the setting for all existing Conan and Generic (#20414) packages. --- models/migrations/migrations.go | 2 ++ models/migrations/v226.go | 15 +++++++++++++++ routers/api/packages/conan/conan.go | 3 +-- tests/integration/api_packages_conan_test.go | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 models/migrations/v226.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 28ffc99886..2a38772180 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -413,6 +413,8 @@ var migrations = []Migration{ NewMigration("Add badges to users", createUserBadgesTable), // v225 -> v226 NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText), + // v226 -> v227 + NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v226.go b/models/migrations/v226.go new file mode 100644 index 0000000000..2f85bca21f --- /dev/null +++ b/models/migrations/v226.go @@ -0,0 +1,15 @@ +// 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 migrations + +import ( + "xorm.io/builder" + "xorm.io/xorm" +) + +func fixPackageSemverField(x *xorm.Engine) error { + _, err := x.Exec(builder.Update(builder.Eq{"semver_compatible": false}).From("`package`").Where(builder.In("`type`", "conan", "generic"))) + return err +} diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go index 90924c7c4b..c57f80ac2f 100644 --- a/routers/api/packages/conan/conan.go +++ b/routers/api/packages/conan/conan.go @@ -342,8 +342,7 @@ func uploadFile(ctx *context.Context, fileFilter stringSet, fileKey string) { Name: rref.Name, Version: rref.Version, }, - SemverCompatible: true, - Creator: ctx.Doer, + Creator: ctx.Doer, } pfci := &packages_service.PackageFileCreationInfo{ PackageFileInfo: packages_service.PackageFileInfo{ diff --git a/tests/integration/api_packages_conan_test.go b/tests/integration/api_packages_conan_test.go index 5b34417343..40c8b70883 100644 --- a/tests/integration/api_packages_conan_test.go +++ b/tests/integration/api_packages_conan_test.go @@ -267,7 +267,7 @@ func TestPackageConan(t *testing.T) { pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) assert.NoError(t, err) - assert.NotNil(t, pd.SemVer) + assert.Nil(t, pd.SemVer) assert.Equal(t, name, pd.Package.Name) assert.Equal(t, version1, pd.Version.Version) assert.IsType(t, &conan_module.Metadata{}, pd.Metadata) From 81d7270cde2960d52f5d987ec8b3fc3c679724a6 Mon Sep 17 00:00:00 2001 From: delvh Date: Fri, 7 Oct 2022 11:48:03 +0200 Subject: [PATCH 17/60] Add new CSS variables --color-accent and --color-small-accent (#21305) At the moment, this is only used to replace the color of the `viewed` checkbox and of the `has changed` label. Previously, the used variable accentuated always either darker or lighter, which meant that one theme looked good while the other didn't. Co-authored-by: silverwind --- assets/go-licenses.json | 8 ++++---- web_src/less/_base.less | 3 +++ web_src/less/_review.less | 15 ++++++++------- web_src/less/themes/theme-arc-green.less | 2 ++ 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 8d129557de..087e62d7d4 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -75,8 +75,8 @@ "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2016 by the authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n================================================================================\n\nPortions of runcontainer.go are from the Go standard library, which is licensed\nunder:\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the following disclaimer\n in the documentation and/or other materials provided with the\n distribution.\n * Neither the name of Google Inc. nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, { - "name": "github.com/alecthomas/chroma", - "path": "github.com/alecthomas/chroma/COPYING", + "name": "github.com/alecthomas/chroma/v2", + "path": "github.com/alecthomas/chroma/v2/COPYING", "licenseText": "Copyright (C) 2017 Alec Thomas\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, { @@ -785,8 +785,8 @@ "licenseText": "Copyright 2015 Yohann Coppel\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n" }, { - "name": "github.com/yuin/goldmark-highlighting", - "path": "github.com/yuin/goldmark-highlighting/LICENSE", + "name": "github.com/yuin/goldmark-highlighting/v2", + "path": "github.com/yuin/goldmark-highlighting/v2/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2019 Yusuke Inuzuka\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, { diff --git a/web_src/less/_base.less b/web_src/less/_base.less index a2c27acfdf..41b145ac7d 100644 --- a/web_src/less/_base.less +++ b/web_src/less/_base.less @@ -159,6 +159,9 @@ --color-tooltip-text: #ffffff; --color-header-bar: #ffffff; --color-label-active-bg: #d0d0d0; + /* accent */ + --color-small-accent: var(--color-primary-light-6); + --color-accent: var(--color-primary-light-4); /* backgrounds */ --checkbox-mask-checked: url('data:image/svg+xml;utf8,'); --checkbox-mask-indeterminate: url('data:image/svg+xml;utf8,'); diff --git a/web_src/less/_review.less b/web_src/less/_review.less index fd18ecb3d3..33e4003b2c 100644 --- a/web_src/less/_review.less +++ b/web_src/less/_review.less @@ -264,11 +264,12 @@ a.blob-excerpt:hover { } .changed-since-last-review { - margin: 0 5px; - padding: 0 3px; - border: 2px var(--color-primary-light-3) solid; - background-color: var(--color-primary-alpha-30); - border-radius: 7px; + border: 1px var(--color-accent) solid; + background-color: var(--color-small-accent); + border-radius: 15px; + padding: 4px 8px; + margin: -8px 0; // just like other buttons in the diff box header + font-size: .857rem; // just like .ui.tiny.button } .viewed-file-form { @@ -286,8 +287,8 @@ a.blob-excerpt:hover { } .viewed-file-checked-form { - background-color: var(--color-primary-light-6); - border: 1px solid var(--color-primary-light-4); + background-color: var(--color-small-accent); + border: 1px solid var(--color-accent); } #viewed-files-summary { diff --git a/web_src/less/themes/theme-arc-green.less b/web_src/less/themes/theme-arc-green.less index 7ebcb87d9d..3f3d4feae2 100644 --- a/web_src/less/themes/theme-arc-green.less +++ b/web_src/less/themes/theme-arc-green.less @@ -134,6 +134,8 @@ --color-reaction-active-bg: var(--color-primary-alpha-40); --color-header-bar: #2e323e; --color-label-active-bg: #4c525e; + --color-small-accent: var(--color-primary-light-5); + --color-accent: var(--color-primary-light-3); } ::-webkit-calendar-picker-indicator { From d94f15c2fd921722796a4cfdbbf266dd3017525c Mon Sep 17 00:00:00 2001 From: Andrew Imeson Date: Fri, 7 Oct 2022 08:49:30 -0400 Subject: [PATCH 18/60] Make external issue tracker regexp configurable via API (#21338) Fixes #21336 Signed-off-by: Andrew Imeson --- modules/convert/repository.go | 7 ++++--- modules/structs/repo.go | 4 +++- routers/api/v1/repo/repo.go | 7 ++++--- templates/swagger/v1_json.tmpl | 7 ++++++- tests/integration/api_repo_edit_test.go | 18 +++++++++++++++--- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/modules/convert/repository.go b/modules/convert/repository.go index f853163848..09b84afa6c 100644 --- a/modules/convert/repository.go +++ b/modules/convert/repository.go @@ -56,9 +56,10 @@ func innerToRepo(repo *repo_model.Repository, mode perm.AccessMode, isParent boo config := unit.ExternalTrackerConfig() hasIssues = true externalTracker = &api.ExternalTracker{ - ExternalTrackerURL: config.ExternalTrackerURL, - ExternalTrackerFormat: config.ExternalTrackerFormat, - ExternalTrackerStyle: config.ExternalTrackerStyle, + ExternalTrackerURL: config.ExternalTrackerURL, + ExternalTrackerFormat: config.ExternalTrackerFormat, + ExternalTrackerStyle: config.ExternalTrackerStyle, + ExternalTrackerRegexpPattern: config.ExternalTrackerRegexpPattern, } } hasWiki := false diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 27e7f4618c..cf6601704e 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -34,8 +34,10 @@ type ExternalTracker struct { ExternalTrackerURL string `json:"external_tracker_url"` // External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index. ExternalTrackerFormat string `json:"external_tracker_format"` - // External Issue Tracker Number Format, either `numeric` or `alphanumeric` + // External Issue Tracker Number Format, either `numeric`, `alphanumeric`, or `regexp` ExternalTrackerStyle string `json:"external_tracker_style"` + // External Issue Tracker issue regular expression + ExternalTrackerRegexpPattern string `json:"external_tracker_regexp_pattern"` } // ExternalWiki represents setting for external wiki diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 319c4c781a..de8a4d1864 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -755,9 +755,10 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { RepoID: repo.ID, Type: unit_model.TypeExternalTracker, Config: &repo_model.ExternalTrackerConfig{ - ExternalTrackerURL: opts.ExternalTracker.ExternalTrackerURL, - ExternalTrackerFormat: opts.ExternalTracker.ExternalTrackerFormat, - ExternalTrackerStyle: opts.ExternalTracker.ExternalTrackerStyle, + ExternalTrackerURL: opts.ExternalTracker.ExternalTrackerURL, + ExternalTrackerFormat: opts.ExternalTracker.ExternalTrackerFormat, + ExternalTrackerStyle: opts.ExternalTracker.ExternalTrackerStyle, + ExternalTrackerRegexpPattern: opts.ExternalTracker.ExternalTrackerRegexpPattern, }, }) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 5ca3794710..728e88b734 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -16015,8 +16015,13 @@ "type": "string", "x-go-name": "ExternalTrackerFormat" }, + "external_tracker_regexp_pattern": { + "description": "External Issue Tracker issue regular expression", + "type": "string", + "x-go-name": "ExternalTrackerRegexpPattern" + }, "external_tracker_style": { - "description": "External Issue Tracker Number Format, either `numeric` or `alphanumeric`", + "description": "External Issue Tracker Number Format, either `numeric`, `alphanumeric`, or `regexp`", "type": "string", "x-go-name": "ExternalTrackerStyle" }, diff --git a/tests/integration/api_repo_edit_test.go b/tests/integration/api_repo_edit_test.go index 5ef92bf47c..4dfae97e43 100644 --- a/tests/integration/api_repo_edit_test.go +++ b/tests/integration/api_repo_edit_test.go @@ -40,9 +40,10 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption config := unit.ExternalTrackerConfig() hasIssues = true externalTracker = &api.ExternalTracker{ - ExternalTrackerURL: config.ExternalTrackerURL, - ExternalTrackerFormat: config.ExternalTrackerFormat, - ExternalTrackerStyle: config.ExternalTrackerStyle, + ExternalTrackerURL: config.ExternalTrackerURL, + ExternalTrackerFormat: config.ExternalTrackerFormat, + ExternalTrackerStyle: config.ExternalTrackerStyle, + ExternalTrackerRegexpPattern: config.ExternalTrackerRegexpPattern, } } hasWiki := false @@ -220,6 +221,17 @@ func TestAPIRepoEdit(t *testing.T) { assert.Equal(t, *repo1editedOption.HasWiki, true) assert.Equal(t, *repo1editedOption.ExternalWiki, *repoEditOption.ExternalWiki) + repoEditOption.ExternalTracker.ExternalTrackerStyle = "regexp" + repoEditOption.ExternalTracker.ExternalTrackerRegexpPattern = `(\d+)` + req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption) + resp = session.MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &repo) + assert.NotNil(t, repo) + repo1edited = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + repo1editedOption = getRepoEditOptionFromRepo(repo1edited) + assert.Equal(t, *repo1editedOption.HasIssues, true) + assert.Equal(t, *repo1editedOption.ExternalTracker, *repoEditOption.ExternalTracker) + // Do some tests with invalid URL for external tracker and wiki repoEditOption.ExternalTracker.ExternalTrackerURL = "htp://www.somewebsite.com" req = NewRequestWithJSON(t, "PATCH", url, &repoEditOption) From 69fc510d6dcf9cda7993eae8cd5c7725b345a9a1 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Fri, 7 Oct 2022 17:30:59 +0200 Subject: [PATCH 19/60] Add GET and DELETE endpoints for Docker blob uploads (#21367) This PR adds support for https://docs.docker.com/registry/spec/api/#get-blob-upload https://docs.docker.com/registry/spec/api/#delete-blob-upload Both are not required by the OCI spec but some clients call these endpoints. Co-authored-by: wxiaoguang --- routers/api/packages/api.go | 12 +++-- routers/api/packages/container/container.go | 45 +++++++++++++++++++ .../api_packages_container_test.go | 40 ++++++++++++++++- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 3354fe12d4..0889006dd7 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -316,8 +316,10 @@ func ContainerRoutes(ctx gocontext.Context) *web.Route { r.Group("/blobs/uploads", func() { r.Post("", container.InitiateUploadBlob) r.Group("/{uuid}", func() { + r.Get("", container.GetUploadBlob) r.Patch("", container.UploadBlob) r.Put("", container.EndUploadBlob) + r.Delete("", container.CancelUploadBlob) }) }, reqPackageAccess(perm.AccessModeWrite)) r.Group("/blobs/{digest}", func() { @@ -377,7 +379,7 @@ func ContainerRoutes(ctx gocontext.Context) *web.Route { } m := blobsUploadsPattern.FindStringSubmatch(path) - if len(m) == 3 && (isPut || isPatch) { + if len(m) == 3 && (isGet || isPut || isPatch || isDelete) { reqPackageAccess(perm.AccessModeWrite)(ctx) if ctx.Written() { return @@ -391,10 +393,14 @@ func ContainerRoutes(ctx gocontext.Context) *web.Route { ctx.SetParams("uuid", m[2]) - if isPatch { + if isGet { + container.GetUploadBlob(ctx) + } else if isPatch { container.UploadBlob(ctx) - } else { + } else if isPut { container.EndUploadBlob(ctx) + } else { + container.CancelUploadBlob(ctx) } return } diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index b961cd4afb..5bc64e1b29 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -248,6 +248,27 @@ func InitiateUploadBlob(ctx *context.Context) { }) } +// https://docs.docker.com/registry/spec/api/#get-blob-upload +func GetUploadBlob(ctx *context.Context) { + uuid := ctx.Params("uuid") + + upload, err := packages_model.GetBlobUploadByID(ctx, uuid) + if err != nil { + if err == packages_model.ErrPackageBlobUploadNotExist { + apiErrorDefined(ctx, errBlobUploadUnknown) + } else { + apiError(ctx, http.StatusInternalServerError, err) + } + return + } + + setResponseHeaders(ctx.Resp, &containerHeaders{ + Range: fmt.Sprintf("0-%d", upload.BytesReceived), + UploadUUID: upload.ID, + Status: http.StatusNoContent, + }) +} + // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks func UploadBlob(ctx *context.Context) { image := ctx.Params("image") @@ -354,6 +375,30 @@ func EndUploadBlob(ctx *context.Context) { }) } +// https://docs.docker.com/registry/spec/api/#delete-blob-upload +func CancelUploadBlob(ctx *context.Context) { + uuid := ctx.Params("uuid") + + _, err := packages_model.GetBlobUploadByID(ctx, uuid) + if err != nil { + if err == packages_model.ErrPackageBlobUploadNotExist { + apiErrorDefined(ctx, errBlobUploadUnknown) + } else { + apiError(ctx, http.StatusInternalServerError, err) + } + return + } + + if err := container_service.RemoveBlobUploadByID(ctx, uuid); err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + setResponseHeaders(ctx.Resp, &containerHeaders{ + Status: http.StatusNoContent, + }) +} + func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescriptor, error) { digest := ctx.Params("digest") diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go index adced5d661..b8a3884371 100644 --- a/tests/integration/api_packages_container_test.go +++ b/tests/integration/api_packages_container_test.go @@ -205,18 +205,54 @@ func TestPackageContainer(t *testing.T) { assert.Equal(t, uuid, resp.Header().Get("Docker-Upload-Uuid")) assert.Equal(t, contentRange, resp.Header().Get("Range")) + uploadURL = resp.Header().Get("Location") + + req = NewRequest(t, "GET", setting.AppURL+uploadURL[1:]) + addTokenAuthHeader(req, userToken) + resp = MakeRequest(t, req, http.StatusNoContent) + + assert.Equal(t, uuid, resp.Header().Get("Docker-Upload-Uuid")) + assert.Equal(t, fmt.Sprintf("0-%d", len(blobContent)), resp.Header().Get("Range")) + pbu, err = packages_model.GetBlobUploadByID(db.DefaultContext, uuid) assert.NoError(t, err) assert.EqualValues(t, len(blobContent), pbu.BytesReceived) - uploadURL = resp.Header().Get("Location") - req = NewRequest(t, "PUT", fmt.Sprintf("%s?digest=%s", setting.AppURL+uploadURL[1:], blobDigest)) addTokenAuthHeader(req, userToken) resp = MakeRequest(t, req, http.StatusCreated) assert.Equal(t, fmt.Sprintf("/v2/%s/%s/blobs/%s", user.Name, image, blobDigest), resp.Header().Get("Location")) assert.Equal(t, blobDigest, resp.Header().Get("Docker-Content-Digest")) + + t.Run("Cancel", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "POST", fmt.Sprintf("%s/blobs/uploads", url)) + addTokenAuthHeader(req, userToken) + resp := MakeRequest(t, req, http.StatusAccepted) + + uuid := resp.Header().Get("Docker-Upload-Uuid") + assert.NotEmpty(t, uuid) + + uploadURL := resp.Header().Get("Location") + assert.NotEmpty(t, uploadURL) + + req = NewRequest(t, "GET", setting.AppURL+uploadURL[1:]) + addTokenAuthHeader(req, userToken) + resp = MakeRequest(t, req, http.StatusNoContent) + + assert.Equal(t, uuid, resp.Header().Get("Docker-Upload-Uuid")) + assert.Equal(t, "0-0", resp.Header().Get("Range")) + + req = NewRequest(t, "DELETE", setting.AppURL+uploadURL[1:]) + addTokenAuthHeader(req, userToken) + MakeRequest(t, req, http.StatusNoContent) + + req = NewRequest(t, "GET", setting.AppURL+uploadURL[1:]) + addTokenAuthHeader(req, userToken) + MakeRequest(t, req, http.StatusNotFound) + }) }) for _, tag := range tags { From c08e42c47ef2a32b3b7ee422c73d6929c93b199e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 8 Oct 2022 01:20:53 +0800 Subject: [PATCH 20/60] Refactor parseTreeEntries, speed up tree list (#21368) Close #20315 (fix the panic when parsing invalid input), Speed up #20231 (use ls-tree without size field) Introduce ListEntriesRecursiveFast (ls-tree without size) and ListEntriesRecursiveWithSize (ls-tree with size) --- modules/git/parse_nogogit.go | 78 +++++++++++----------- modules/git/parse_nogogit_test.go | 48 +++++++++++-- modules/git/repo_language_stats_nogogit.go | 2 +- modules/git/tree_gogit.go | 9 ++- modules/git/tree_nogogit.go | 19 +++++- routers/web/repo/treelist.go | 4 +- services/repository/files/tree.go | 2 +- 7 files changed, 109 insertions(+), 53 deletions(-) diff --git a/modules/git/parse_nogogit.go b/modules/git/parse_nogogit.go index c8f0f994fc..fb5b63def9 100644 --- a/modules/git/parse_nogogit.go +++ b/modules/git/parse_nogogit.go @@ -22,70 +22,72 @@ func ParseTreeEntries(data []byte) ([]*TreeEntry, error) { return parseTreeEntries(data, nil) } +var sepSpace = []byte{' '} + func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) { - entries := make([]*TreeEntry, 0, 10) + var err error + entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1) for pos := 0; pos < len(data); { - // expect line to be of the form " \t" + // expect line to be of the form: + // \t + // \t + posEnd := bytes.IndexByte(data[pos:], '\n') + if posEnd == -1 { + posEnd = len(data) + } else { + posEnd += pos + } + line := data[pos:posEnd] + posTab := bytes.IndexByte(line, '\t') + if posTab == -1 { + return nil, fmt.Errorf("invalid ls-tree output (no tab): %q", line) + } + entry := new(TreeEntry) entry.ptree = ptree - if pos+6 > len(data) { - return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data)) + + entryAttrs := line[:posTab] + entryName := line[posTab+1:] + + entryMode, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace) + _ /* entryType */, entryAttrs, _ = bytes.Cut(entryAttrs, sepSpace) // the type is not used, the mode is enough to determine the type + entryObjectID, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace) + if len(entryAttrs) > 0 { + entrySize := entryAttrs // the last field is the space-padded-size + entry.size, _ = strconv.ParseInt(strings.TrimSpace(string(entrySize)), 10, 64) + entry.sized = true } - switch string(data[pos : pos+6]) { + + switch string(entryMode) { case "100644": entry.entryMode = EntryModeBlob - pos += 12 // skip over "100644 blob " case "100755": entry.entryMode = EntryModeExec - pos += 12 // skip over "100755 blob " case "120000": entry.entryMode = EntryModeSymlink - pos += 12 // skip over "120000 blob " case "160000": entry.entryMode = EntryModeCommit - pos += 14 // skip over "160000 object " case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons entry.entryMode = EntryModeTree - pos += 12 // skip over "040000 tree " default: - return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+6])) + return nil, fmt.Errorf("unknown type: %v", string(entryMode)) } - if pos+40 > len(data) { - return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data)) - } - id, err := NewIDFromString(string(data[pos : pos+40])) + entry.ID, err = NewIDFromString(string(entryObjectID)) if err != nil { - return nil, fmt.Errorf("Invalid ls-tree output: %v", err) - } - entry.ID = id - pos += 41 // skip over sha and trailing space - - end := pos + bytes.IndexByte(data[pos:], '\t') - if end < pos { - return nil, fmt.Errorf("Invalid ls-tree -l output: %s", string(data)) - } - entry.size, _ = strconv.ParseInt(strings.TrimSpace(string(data[pos:end])), 10, 64) - entry.sized = true - - pos = end + 1 - - end = pos + bytes.IndexByte(data[pos:], '\n') - if end < pos { - return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data)) + return nil, fmt.Errorf("invalid ls-tree output (invalid object id): %q, err: %w", line, err) } - // In case entry name is surrounded by double quotes(it happens only in git-shell). - if data[pos] == '"' { - entry.name, err = strconv.Unquote(string(data[pos:end])) + if len(entryName) > 0 && entryName[0] == '"' { + entry.name, err = strconv.Unquote(string(entryName)) if err != nil { - return nil, fmt.Errorf("Invalid ls-tree output: %v", err) + return nil, fmt.Errorf("invalid ls-tree output (invalid name): %q, err: %w", line, err) } } else { - entry.name = string(data[pos:end]) + entry.name = string(entryName) } - pos = end + 1 + pos = posEnd + 1 entries = append(entries, entry) } return entries, nil diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go index 483f96e9a7..cecd3960da 100644 --- a/modules/git/parse_nogogit_test.go +++ b/modules/git/parse_nogogit_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestParseTreeEntries(t *testing.T) { +func TestParseTreeEntriesLong(t *testing.T) { testCases := []struct { Input string Expected []*TreeEntry @@ -59,11 +59,47 @@ func TestParseTreeEntries(t *testing.T) { assert.NoError(t, err) assert.Len(t, entries, len(testCase.Expected)) for i, entry := range entries { - assert.EqualValues(t, testCase.Expected[i].ID, entry.ID) - assert.EqualValues(t, testCase.Expected[i].name, entry.name) - assert.EqualValues(t, testCase.Expected[i].entryMode, entry.entryMode) - assert.EqualValues(t, testCase.Expected[i].sized, entry.sized) - assert.EqualValues(t, testCase.Expected[i].size, entry.size) + assert.EqualValues(t, testCase.Expected[i], entry) } } } + +func TestParseTreeEntriesShort(t *testing.T) { + testCases := []struct { + Input string + Expected []*TreeEntry + }{ + { + Input: `100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af README.md +040000 tree 84b90550547016f73c5dd3f50dea662389e67b6d assets +`, + Expected: []*TreeEntry{ + { + ID: MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"), + name: "README.md", + entryMode: EntryModeBlob, + }, + { + ID: MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"), + name: "assets", + entryMode: EntryModeTree, + }, + }, + }, + } + for _, testCase := range testCases { + entries, err := ParseTreeEntries([]byte(testCase.Input)) + assert.NoError(t, err) + assert.Len(t, entries, len(testCase.Expected)) + for i, entry := range entries { + assert.EqualValues(t, testCase.Expected[i], entry) + } + } +} + +func TestParseTreeEntriesInvalid(t *testing.T) { + // there was a panic: "runtime error: slice bounds out of range" when the input was invalid: #20315 + entries, err := ParseTreeEntries([]byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af")) + assert.Error(t, err) + assert.Len(t, entries, 0) +} diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/repo_language_stats_nogogit.go index d237924f92..7388ef403b 100644 --- a/modules/git/repo_language_stats_nogogit.go +++ b/modules/git/repo_language_stats_nogogit.go @@ -57,7 +57,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err tree := commit.Tree - entries, err := tree.ListEntriesRecursive() + entries, err := tree.ListEntriesRecursiveWithSize() if err != nil { return nil, err } diff --git a/modules/git/tree_gogit.go b/modules/git/tree_gogit.go index 54f8e140fb..480c5e44da 100644 --- a/modules/git/tree_gogit.go +++ b/modules/git/tree_gogit.go @@ -57,8 +57,8 @@ func (t *Tree) ListEntries() (Entries, error) { return entries, nil } -// ListEntriesRecursive returns all entries of current tree recursively including all subtrees -func (t *Tree) ListEntriesRecursive() (Entries, error) { +// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees +func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) { if t.gogitTree == nil { err := t.loadTreeObject() if err != nil { @@ -92,3 +92,8 @@ func (t *Tree) ListEntriesRecursive() (Entries, error) { return entries, nil } + +// ListEntriesRecursiveFast is the alias of ListEntriesRecursiveWithSize for the gogit version +func (t *Tree) ListEntriesRecursiveFast() (Entries, error) { + return t.ListEntriesRecursiveWithSize() +} diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go index 7defb064a4..d61d19e4c2 100644 --- a/modules/git/tree_nogogit.go +++ b/modules/git/tree_nogogit.go @@ -99,13 +99,16 @@ func (t *Tree) ListEntries() (Entries, error) { return t.entries, err } -// ListEntriesRecursive returns all entries of current tree recursively including all subtrees -func (t *Tree) ListEntriesRecursive() (Entries, error) { +// listEntriesRecursive returns all entries of current tree recursively including all subtrees +// extraArgs could be "-l" to get the size, which is slower +func (t *Tree) listEntriesRecursive(extraArgs ...string) (Entries, error) { if t.entriesRecursiveParsed { return t.entriesRecursive, nil } - stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-t", "-l", "-r", t.ID.String()).RunStdBytes(&RunOpts{Dir: t.repo.Path}) + args := append([]string{"ls-tree", "-t", "-r"}, extraArgs...) + args = append(args, t.ID.String()) + stdout, _, runErr := NewCommand(t.repo.Ctx, args...).RunStdBytes(&RunOpts{Dir: t.repo.Path}) if runErr != nil { return nil, runErr } @@ -118,3 +121,13 @@ func (t *Tree) ListEntriesRecursive() (Entries, error) { return t.entriesRecursive, err } + +// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size +func (t *Tree) ListEntriesRecursiveFast() (Entries, error) { + return t.listEntriesRecursive() +} + +// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with size +func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) { + return t.listEntriesRecursive("--long") +} diff --git a/routers/web/repo/treelist.go b/routers/web/repo/treelist.go index 35ac0d507f..80f43a0c40 100644 --- a/routers/web/repo/treelist.go +++ b/routers/web/repo/treelist.go @@ -22,9 +22,9 @@ func TreeList(ctx *context.Context) { return } - entries, err := tree.ListEntriesRecursive() + entries, err := tree.ListEntriesRecursiveFast() if err != nil { - ctx.ServerError("ListEntriesRecursive", err) + ctx.ServerError("ListEntriesRecursiveFast", err) return } entries.CustomSort(base.NaturalSortLess) diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go index caad732887..59e5690977 100644 --- a/services/repository/files/tree.go +++ b/services/repository/files/tree.go @@ -29,7 +29,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git tree.URL = repo.APIURL() + "/git/trees/" + url.PathEscape(tree.SHA) var entries git.Entries if recursive { - entries, err = gitTree.ListEntriesRecursive() + entries, err = gitTree.ListEntriesRecursiveWithSize() } else { entries, err = gitTree.ListEntries() } From 56aabf3e8dbbf44c73766ad915cc4808d594b48e Mon Sep 17 00:00:00 2001 From: rj1 Date: Fri, 7 Oct 2022 13:12:19 -0500 Subject: [PATCH 21/60] Fix some typos and update db transaction demo in backend guideline (#21322) Co-authored-by: wxiaoguang --- .../doc/developers/guidelines-backend.md | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/docs/content/doc/developers/guidelines-backend.md b/docs/content/doc/developers/guidelines-backend.md index b680d73c82..913898be83 100644 --- a/docs/content/doc/developers/guidelines-backend.md +++ b/docs/content/doc/developers/guidelines-backend.md @@ -67,22 +67,18 @@ Some actions should allow for rollback when database record insertion/update/del So services must be allowed to create a database transaction. Here is some example, ```go -// servcies/repository/repo.go -func CreateXXXX() error {\ - ctx, committer, err := db.TxContext() - if err != nil { - return err - } - defer committer.Close() - - // do something, if return err, it will rollback automatically when `committer.Close()` is invoked. - if err := issues.UpdateIssue(ctx, repoID); err != nil { - // ... - } - - // ...... - - return committer.Commit() +// services/repository/repository.go +func CreateXXXX() error { + return db.WithTx(func(ctx context.Context) error { + e := db.GetEngine(ctx) + // do something, if err is returned, it will rollback automatically + if err := issues.UpdateIssue(ctx, repoID); err != nil { + // ... + return err + } + // ... + return nil + }) } ``` @@ -94,14 +90,14 @@ If the function will be used in the transaction, just let `context.Context` as t func UpdateIssue(ctx context.Context, repoID int64) error { e := db.GetEngine(ctx) - // ...... + // ... } ``` ### Package Name For the top level package, use a plural as package name, i.e. `services`, `models`, for sub packages, use singular, -i.e. `servcies/user`, `models/repository`. +i.e. `services/user`, `models/repository`. ### Import Alias From 7d2545d183058f98e96efb1bd972c2b841eebf43 Mon Sep 17 00:00:00 2001 From: zeripath Date: Fri, 7 Oct 2022 22:02:24 +0100 Subject: [PATCH 22/60] Add nicer error handling on template compile errors (#21350) There are repeated issues reported whereby users are unable to interpret the template errors. This PR adds some (somewhat complex) error handling to the panic recovery for template renderering but hopefully makes the interpretation of the error easier. Reference #21344 Signed-off-by: Andrew Thornton --- modules/templates/dynamic.go | 15 +++ modules/templates/htmlrenderer.go | 199 +++++++++++++++++++++++++++++- modules/templates/static.go | 12 ++ 3 files changed, 225 insertions(+), 1 deletion(-) diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go index 4896580f62..a86e71a8c8 100644 --- a/modules/templates/dynamic.go +++ b/modules/templates/dynamic.go @@ -33,6 +33,21 @@ func GetAsset(name string) ([]byte, error) { return os.ReadFile(filepath.Join(setting.StaticRootPath, name)) } +// GetAssetFilename returns the filename of the provided asset +func GetAssetFilename(name string) (string, error) { + filename := filepath.Join(setting.CustomPath, name) + _, err := os.Stat(filename) + if err != nil && !os.IsNotExist(err) { + return filename, err + } else if err == nil { + return filename, nil + } + + filename = filepath.Join(setting.StaticRootPath, name) + _, err = os.Stat(filename) + return filename, err +} + // walkTemplateFiles calls a callback for each template asset func walkTemplateFiles(callback func(path, name string, d fs.DirEntry, err error) error) error { if err := walkAssetDir(filepath.Join(setting.CustomPath, "templates"), true, callback); err != nil && !os.IsNotExist(err) { diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index 210bb5e73c..81ea660161 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -5,7 +5,12 @@ package templates import ( + "bytes" "context" + "fmt" + "regexp" + "strconv" + "strings" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -14,7 +19,14 @@ import ( "github.com/unrolled/render" ) -var rendererKey interface{} = "templatesHtmlRendereer" +var ( + rendererKey interface{} = "templatesHtmlRenderer" + + templateError = regexp.MustCompile(`^template: (.*):([0-9]+): (.*)`) + notDefinedError = regexp.MustCompile(`^template: (.*):([0-9]+): function "(.*)" not defined`) + unexpectedError = regexp.MustCompile(`^template: (.*):([0-9]+): unexpected "(.*)" in operand`) + expectedEndError = regexp.MustCompile(`^template: (.*):([0-9]+): expected end; found (.*)`) +) // HTMLRenderer returns the current html renderer for the context or creates and stores one within the context for future use func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) { @@ -32,6 +44,25 @@ func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) { } log.Log(1, log.DEBUG, "Creating "+rendererType+" HTML Renderer") + compilingTemplates := true + defer func() { + if !compilingTemplates { + return + } + + panicked := recover() + if panicked == nil { + return + } + + // OK try to handle the panic... + err, ok := panicked.(error) + if ok { + handlePanicError(err) + } + log.Fatal("PANIC: Unable to compile templates!\n%v\n\nStacktrace:\n%s", panicked, log.Stack(2)) + }() + renderer := render.New(render.Options{ Extensions: []string{".tmpl"}, Directory: "templates", @@ -42,6 +73,7 @@ func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) { IsDevelopment: false, DisableHTTPErrorRendering: true, }) + compilingTemplates = false if !setting.IsProd { watcher.CreateWatcher(ctx, "HTML Templates", &watcher.CreateWatcherOpts{ PathsCallback: walkTemplateFiles, @@ -50,3 +82,168 @@ func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) { } return context.WithValue(ctx, rendererKey, renderer), renderer } + +func handlePanicError(err error) { + wrapFatal(handleNotDefinedPanicError(err)) + wrapFatal(handleUnexpected(err)) + wrapFatal(handleExpectedEnd(err)) + wrapFatal(handleGenericTemplateError(err)) +} + +func wrapFatal(format string, args []interface{}) { + if format == "" { + return + } + log.FatalWithSkip(1, format, args...) +} + +func handleGenericTemplateError(err error) (string, []interface{}) { + groups := templateError.FindStringSubmatch(err.Error()) + if len(groups) != 4 { + return "", nil + } + + templateName, lineNumberStr, message := groups[1], groups[2], groups[3] + + filename, assetErr := GetAssetFilename("templates/" + templateName + ".tmpl") + if assetErr != nil { + return "", nil + } + + lineNumber, _ := strconv.Atoi(lineNumberStr) + + line := getLineFromAsset(templateName, lineNumber, "") + + return "PANIC: Unable to compile templates!\n%s in template file %s at line %d:\n\n%s\nStacktrace:\n\n%s", []interface{}{message, filename, lineNumber, log.NewColoredValue(line, log.Reset), log.Stack(2)} +} + +func handleNotDefinedPanicError(err error) (string, []interface{}) { + groups := notDefinedError.FindStringSubmatch(err.Error()) + if len(groups) != 4 { + return "", nil + } + + templateName, lineNumberStr, functionName := groups[1], groups[2], groups[3] + + functionName, _ = strconv.Unquote(`"` + functionName + `"`) + + filename, assetErr := GetAssetFilename("templates/" + templateName + ".tmpl") + if assetErr != nil { + return "", nil + } + + lineNumber, _ := strconv.Atoi(lineNumberStr) + + line := getLineFromAsset(templateName, lineNumber, functionName) + + return "PANIC: Unable to compile templates!\nUndefined function %q in template file %s at line %d:\n\n%s", []interface{}{functionName, filename, lineNumber, log.NewColoredValue(line, log.Reset)} +} + +func handleUnexpected(err error) (string, []interface{}) { + groups := unexpectedError.FindStringSubmatch(err.Error()) + if len(groups) != 4 { + return "", nil + } + + templateName, lineNumberStr, unexpected := groups[1], groups[2], groups[3] + unexpected, _ = strconv.Unquote(`"` + unexpected + `"`) + + filename, assetErr := GetAssetFilename("templates/" + templateName + ".tmpl") + if assetErr != nil { + return "", nil + } + + lineNumber, _ := strconv.Atoi(lineNumberStr) + + line := getLineFromAsset(templateName, lineNumber, unexpected) + + return "PANIC: Unable to compile templates!\nUnexpected %q in template file %s at line %d:\n\n%s", []interface{}{unexpected, filename, lineNumber, log.NewColoredValue(line, log.Reset)} +} + +func handleExpectedEnd(err error) (string, []interface{}) { + groups := expectedEndError.FindStringSubmatch(err.Error()) + if len(groups) != 4 { + return "", nil + } + + templateName, lineNumberStr, unexpected := groups[1], groups[2], groups[3] + + filename, assetErr := GetAssetFilename("templates/" + templateName + ".tmpl") + if assetErr != nil { + return "", nil + } + + lineNumber, _ := strconv.Atoi(lineNumberStr) + + line := getLineFromAsset(templateName, lineNumber, unexpected) + + return "PANIC: Unable to compile templates!\nMissing end with unexpected %q in template file %s at line %d:\n\n%s", []interface{}{unexpected, filename, lineNumber, log.NewColoredValue(line, log.Reset)} +} + +const dashSeparator = "----------------------------------------------------------------------\n" + +func getLineFromAsset(templateName string, targetLineNum int, target string) string { + bs, err := GetAsset("templates/" + templateName + ".tmpl") + if err != nil { + return fmt.Sprintf("(unable to read template file: %v)", err) + } + + sb := &strings.Builder{} + + // Write the header + sb.WriteString(dashSeparator) + + var lineBs []byte + + // Iterate through the lines from the asset file to find the target line + for start, currentLineNum := 0, 1; currentLineNum <= targetLineNum && start < len(bs); currentLineNum++ { + // Find the next new line + end := bytes.IndexByte(bs[start:], '\n') + + // adjust the end to be a direct pointer in to []byte + if end < 0 { + end = len(bs) + } else { + end += start + } + + // set lineBs to the current line []byte + lineBs = bs[start:end] + + // move start to after the current new line position + start = end + 1 + + // Write 2 preceding lines + the target line + if targetLineNum-currentLineNum < 3 { + _, _ = sb.Write(lineBs) + _ = sb.WriteByte('\n') + } + } + + // If there is a provided target to look for in the line add a pointer to it + // e.g. ^^^^^^^ + if target != "" { + idx := bytes.Index(lineBs, []byte(target)) + + if idx >= 0 { + // take the current line and replace preceding text with whitespace (except for tab) + for i := range lineBs[:idx] { + if lineBs[i] != '\t' { + lineBs[i] = ' ' + } + } + + // write the preceding "space" + _, _ = sb.Write(lineBs[:idx]) + + // Now write the ^^ pointer + _, _ = sb.WriteString(strings.Repeat("^", len(target))) + _ = sb.WriteByte('\n') + } + } + + // Finally write the footer + sb.WriteString(dashSeparator) + + return sb.String() +} diff --git a/modules/templates/static.go b/modules/templates/static.go index 3265bd9cfc..7f7cbe702f 100644 --- a/modules/templates/static.go +++ b/modules/templates/static.go @@ -31,6 +31,18 @@ func GlobalModTime(filename string) time.Time { return timeutil.GetExecutableModTime() } +// GetAssetFilename returns the filename of the provided asset +func GetAssetFilename(name string) (string, error) { + filename := filepath.Join(setting.CustomPath, name) + _, err := os.Stat(filename) + if err != nil && !os.IsNotExist(err) { + return name, err + } else if err == nil { + return filename, nil + } + return "(builtin) " + name, nil +} + // GetAsset get a special asset, only for chi func GetAsset(name string) ([]byte, error) { bs, err := os.ReadFile(filepath.Join(setting.CustomPath, name)) From 223556073c51b34f2ccca7c1036552a4807d1f42 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Fri, 7 Oct 2022 23:06:04 +0200 Subject: [PATCH 23/60] Show private data in feeds (#21369) Show private data in feeds for admins and matching users. --- routers/web/feed/profile.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go index c467f7412a..0e11f210ce 100644 --- a/routers/web/feed/profile.go +++ b/routers/web/feed/profile.go @@ -26,10 +26,12 @@ func ShowUserFeedAtom(ctx *context.Context) { // showUserFeed show user activity as RSS / Atom feed func showUserFeed(ctx *context.Context, formatType string) { + includePrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) + actions, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{ RequestedUser: ctx.ContextUser, Actor: ctx.Doer, - IncludePrivate: false, + IncludePrivate: includePrivate, OnlyPerformedBy: !ctx.ContextUser.IsOrganization(), IncludeDeleted: false, Date: ctx.FormString("date"), From f0aed8205b28d984698414d32635b66927ea6ca0 Mon Sep 17 00:00:00 2001 From: Akshay Mankar Date: Fri, 7 Oct 2022 23:06:34 +0200 Subject: [PATCH 24/60] Fix formatted link for PR review notifications to matrix (#21319) The PR review notifications HTML was written as markdown due to not using `MatrixLinkFormatter`. --- services/webhook/matrix.go | 4 ++-- services/webhook/matrix_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index f4c4cf0e9a..59eae48307 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -195,7 +195,7 @@ func (m *MatrixPayloadUnsafe) PullRequest(p *api.PullRequestPayload) (api.Payloa func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) { senderLink := MatrixLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName) title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) - titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index) + titleLink := MatrixLinkFormatter(p.PullRequest.URL, title) repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) var text string @@ -206,7 +206,7 @@ func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_mo return nil, err } - text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request review %s: %s by %s", repoLink, action, titleLink, senderLink) } return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index 4cab845330..624986ee9b 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -140,7 +140,7 @@ func TestMatrixPayload(t *testing.T) { require.IsType(t, &MatrixPayloadUnsafe{}, pl) assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body) - assert.Equal(t, `[test/repo] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) + assert.Equal(t, `[test/repo] Pull request review approved: #12 Fix bug by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody) }) t.Run("Repository", func(t *testing.T) { From 6c53cf852f1b00fbcd5f641ebf4885f78cf1160c Mon Sep 17 00:00:00 2001 From: Michael Horstmann Date: Sat, 8 Oct 2022 03:53:42 +0000 Subject: [PATCH 25/60] Removed one extra whitespace in footer after "Template" (#21364) --- templates/base/footer_content.tmpl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl index 88dec014f6..89be609225 100644 --- a/templates/base/footer_content.tmpl +++ b/templates/base/footer_content.tmpl @@ -12,8 +12,7 @@ {{end}} {{if and .TemplateLoadTimes ShowFooterTemplateLoadTime}} {{.locale.Tr "page"}}: {{LoadTimes .PageStartTime}} - {{.locale.Tr "template"}} - {{if .TemplateName}} {{.TemplateName}}{{end}}: {{call .TemplateLoadTimes}} + {{.locale.Tr "template"}}{{if .TemplateName}} {{.TemplateName}}{{end}}: {{call .TemplateLoadTimes}} {{end}}