loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request '[gitea] week 2024-24 cherry pick (gitea/main -> forgejo)' (#4083) from earl-warren/wcp/2024-24 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4083
Reviewed-by: twenty-panda <twenty-panda@noreply.codeberg.org>

+532 -226
+5 -3
.devcontainer/devcontainer.json
··· 10 10 "ghcr.io/devcontainers-contrib/features/poetry:2": {}, 11 11 "ghcr.io/devcontainers/features/python:1": { 12 12 "version": "3.12" 13 - } 13 + }, 14 + "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} 14 15 }, 15 16 "customizations": { 16 17 "vscode": { ··· 25 26 "Vue.volar", 26 27 "ms-azuretools.vscode-docker", 27 28 "vitest.explorer", 28 - "qwtel.sqlite-viewer", 29 - "GitHub.vscode-pull-request-github" 29 + "cweijan.vscode-database-client2", 30 + "GitHub.vscode-pull-request-github", 31 + "Azurite.azurite" 30 32 ] 31 33 } 32 34 },
+1 -1
.gitpod.yml
··· 43 43 - Vue.volar 44 44 - ms-azuretools.vscode-docker 45 45 - vitest.explorer 46 - - qwtel.sqlite-viewer 46 + - cweijan.vscode-database-client2 47 47 - GitHub.vscode-pull-request-github 48 48 49 49 ports:
+9 -1
Makefile
··· 38 38 GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go 39 39 DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.22.0 # renovate: datasource=go 40 40 GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go 41 + GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.15.3 # renovate: datasource=go 41 42 42 43 DOCKER_IMAGE ?= gitea/gitea 43 44 DOCKER_TAG ?= latest ··· 228 229 @echo " - lint-go lint go files" 229 230 @echo " - lint-go-fix lint go files and fix issues" 230 231 @echo " - lint-go-vet lint go files with vet" 232 + @echo " - lint-go-gopls lint go files with gopls" 231 233 @echo " - lint-js lint js files" 232 234 @echo " - lint-js-fix lint js files and fix issues" 233 235 @echo " - lint-css lint css files" ··· 468 470 @echo "Running go vet..." 469 471 @$(GO) vet ./... 470 472 473 + .PHONY: lint-go-gopls 474 + lint-go-gopls: 475 + @echo "Running gopls check..." 476 + @GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA) 477 + 471 478 .PHONY: lint-editorconfig 472 479 lint-editorconfig: 473 480 $(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .forgejo/workflows ··· 879 886 $(GO) install $(GO_LICENSES_PACKAGE) 880 887 $(GO) install $(GOVULNCHECK_PACKAGE) 881 888 $(GO) install $(GOMOCK_PACKAGE) 889 + $(GO) install $(GOPLS_PACKAGE) 882 890 883 891 node_modules: package-lock.json 884 892 npm install --no-save 885 893 @touch node_modules 886 894 887 895 .venv: poetry.lock 888 - poetry install --no-root 896 + poetry install 889 897 @touch .venv 890 898 891 899 .PHONY: fomantic
+13
custom/conf/app.example.ini
··· 1387 1387 ;; 1388 1388 ;; Maximum allowed file size in bytes to render CSV files as table. (Set to 0 for no limit). 1389 1389 ;MAX_FILE_SIZE = 524288 1390 + ;; 1391 + ;; Maximum allowed rows to render CSV files. (Set to 0 for no limit) 1392 + ;MAX_ROWS = 2500 1390 1393 1391 1394 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1392 1395 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ··· 1745 1748 ;; 1746 1749 ;; convert \r\n to \n for Sendmail 1747 1750 ;SENDMAIL_CONVERT_CRLF = true 1751 + 1752 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1753 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1754 + ;[mailer.override_header] 1755 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1756 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1757 + ;; This is empty by default, use it only if you know what you need it for. 1758 + ;Reply-To = test@example.com, test2@example.com 1759 + ;Content-Type = text/html; charset=utf-8 1760 + ;In-Reply-To = 1748 1761 1749 1762 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1750 1763 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+35 -59
modules/markup/csv/csv.go
··· 5 5 6 6 import ( 7 7 "bufio" 8 - "bytes" 9 - "fmt" 10 8 "html" 11 9 "io" 12 10 "regexp" ··· 15 13 "code.gitea.io/gitea/modules/csv" 16 14 "code.gitea.io/gitea/modules/markup" 17 15 "code.gitea.io/gitea/modules/setting" 16 + "code.gitea.io/gitea/modules/translation" 17 + "code.gitea.io/gitea/modules/util" 18 18 ) 19 19 20 20 func init() { ··· 81 81 func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { 82 82 tmpBlock := bufio.NewWriter(output) 83 83 maxSize := setting.UI.CSV.MaxFileSize 84 - 85 - if maxSize == 0 { 86 - return r.tableRender(ctx, input, tmpBlock) 87 - } 88 - 89 - rawBytes, err := io.ReadAll(io.LimitReader(input, maxSize+1)) 90 - if err != nil { 91 - return err 92 - } 93 - 94 - if int64(len(rawBytes)) <= maxSize { 95 - return r.tableRender(ctx, bytes.NewReader(rawBytes), tmpBlock) 96 - } 97 - return r.fallbackRender(io.MultiReader(bytes.NewReader(rawBytes), input), tmpBlock) 98 - } 99 - 100 - func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error { 101 - _, err := tmpBlock.WriteString("<pre>") 102 - if err != nil { 103 - return err 104 - } 105 - 106 - scan := bufio.NewScanner(input) 107 - scan.Split(bufio.ScanRunes) 108 - for scan.Scan() { 109 - switch scan.Text() { 110 - case `&`: 111 - _, err = tmpBlock.WriteString("&amp;") 112 - case `'`: 113 - _, err = tmpBlock.WriteString("&#39;") // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5. 114 - case `<`: 115 - _, err = tmpBlock.WriteString("&lt;") 116 - case `>`: 117 - _, err = tmpBlock.WriteString("&gt;") 118 - case `"`: 119 - _, err = tmpBlock.WriteString("&#34;") // "&#34;" is shorter than "&quot;". 120 - default: 121 - _, err = tmpBlock.Write(scan.Bytes()) 122 - } 123 - if err != nil { 124 - return err 125 - } 126 - } 127 - if err = scan.Err(); err != nil { 128 - return fmt.Errorf("fallbackRender scan: %w", err) 129 - } 84 + maxRows := setting.UI.CSV.MaxRows 130 85 131 - _, err = tmpBlock.WriteString("</pre>") 132 - if err != nil { 133 - return err 86 + if maxSize != 0 { 87 + input = io.LimitReader(input, maxSize+1) 134 88 } 135 - return tmpBlock.Flush() 136 - } 137 89 138 - func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock *bufio.Writer) error { 139 90 rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input) 140 91 if err != nil { 141 92 return err 142 93 } 143 - 144 94 if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil { 145 95 return err 146 96 } 147 - row := 1 97 + 98 + row := 0 148 99 for { 149 100 fields, err := rd.Read() 150 - if err == io.EOF { 101 + if err == io.EOF || (row >= maxRows && maxRows != 0) { 151 102 break 152 103 } 153 104 if err != nil { 154 105 continue 155 106 } 107 + 156 108 if _, err := tmpBlock.WriteString("<tr>"); err != nil { 157 109 return err 158 110 } 159 111 element := "td" 160 - if row == 1 { 112 + if row == 0 { 161 113 element = "th" 162 114 } 163 - if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row)); err != nil { 115 + if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row+1)); err != nil { 164 116 return err 165 117 } 166 118 for _, field := range fields { ··· 174 126 175 127 row++ 176 128 } 129 + 177 130 if _, err = tmpBlock.WriteString("</table>"); err != nil { 178 131 return err 179 132 } 133 + 134 + // Check if maxRows or maxSize is reached, and if true, warn. 135 + if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) { 136 + warn := `<table class="data-table"><tr><td>` 137 + rawLink := ` <a href="` + ctx.Links.RawLink() + `/` + util.PathEscapeSegments(ctx.RelativePath) + `">` 138 + 139 + // Try to get the user translation 140 + if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok { 141 + warn += locale.TrString("repo.file_too_large") 142 + rawLink += locale.TrString("repo.file_view_raw") 143 + } else { 144 + warn += "The file is too large to be shown." 145 + rawLink += "View Raw" 146 + } 147 + 148 + warn += rawLink + `</a></td></tr></table>` 149 + 150 + // Write the HTML string to the output 151 + if _, err := tmpBlock.WriteString(warn); err != nil { 152 + return err 153 + } 154 + } 155 + 180 156 return tmpBlock.Flush() 181 157 }
-10
modules/markup/csv/csv_test.go
··· 4 4 package markup 5 5 6 6 import ( 7 - "bufio" 8 - "bytes" 9 7 "strings" 10 8 "testing" 11 9 ··· 31 29 assert.NoError(t, err) 32 30 assert.EqualValues(t, v, buf.String()) 33 31 } 34 - 35 - t.Run("fallbackRender", func(t *testing.T) { 36 - var buf bytes.Buffer 37 - err := render.fallbackRender(strings.NewReader("1,<a>\n2,<b>"), bufio.NewWriter(&buf)) 38 - assert.NoError(t, err) 39 - want := "<pre>1,&lt;a&gt;\n2,&lt;b&gt;</pre>" 40 - assert.Equal(t, want, buf.String()) 41 - }) 42 32 }
+15 -8
modules/setting/mailer.go
··· 18 18 // Mailer represents mail service. 19 19 type Mailer struct { 20 20 // Mailer 21 - Name string `ini:"NAME"` 22 - From string `ini:"FROM"` 23 - EnvelopeFrom string `ini:"ENVELOPE_FROM"` 24 - OverrideEnvelopeFrom bool `ini:"-"` 25 - FromName string `ini:"-"` 26 - FromEmail string `ini:"-"` 27 - SendAsPlainText bool `ini:"SEND_AS_PLAIN_TEXT"` 28 - SubjectPrefix string `ini:"SUBJECT_PREFIX"` 21 + Name string `ini:"NAME"` 22 + From string `ini:"FROM"` 23 + EnvelopeFrom string `ini:"ENVELOPE_FROM"` 24 + OverrideEnvelopeFrom bool `ini:"-"` 25 + FromName string `ini:"-"` 26 + FromEmail string `ini:"-"` 27 + SendAsPlainText bool `ini:"SEND_AS_PLAIN_TEXT"` 28 + SubjectPrefix string `ini:"SUBJECT_PREFIX"` 29 + OverrideHeader map[string][]string `ini:"-"` 29 30 30 31 // SMTP sender 31 32 Protocol string `ini:"PROTOCOL"` ··· 157 158 MailService = &Mailer{} 158 159 if err := sec.MapTo(MailService); err != nil { 159 160 log.Fatal("Unable to map [mailer] section on to MailService. Error: %v", err) 161 + } 162 + 163 + overrideHeader := rootCfg.Section("mailer.override_header").Keys() 164 + MailService.OverrideHeader = make(map[string][]string) 165 + for _, key := range overrideHeader { 166 + MailService.OverrideHeader[key.Name()] = key.Strings(",") 160 167 } 161 168 162 169 // Infer SMTPPort if not set
+3
modules/setting/ui.go
··· 53 53 54 54 CSV struct { 55 55 MaxFileSize int64 56 + MaxRows int 56 57 } `ini:"ui.csv"` 57 58 58 59 Admin struct { ··· 110 111 }, 111 112 CSV: struct { 112 113 MaxFileSize int64 114 + MaxRows int 113 115 }{ 114 116 MaxFileSize: 524288, 117 + MaxRows: 2500, 115 118 }, 116 119 Admin: struct { 117 120 UserPagingNum int
+5 -2
modules/structs/activity.go
··· 6 6 import "time" 7 7 8 8 type Activity struct { 9 - ID int64 `json:"id"` 10 - UserID int64 `json:"user_id"` // Receiver user 9 + ID int64 `json:"id"` 10 + UserID int64 `json:"user_id"` // Receiver user 11 + // the type of action 12 + // 13 + // enum: create_repo,rename_repo,star_repo,watch_repo,commit_repo,create_issue,create_pull_request,transfer_repo,push_tag,comment_issue,merge_pull_request,close_issue,reopen_issue,close_pull_request,reopen_pull_request,delete_tag,delete_branch,mirror_sync_push,mirror_sync_create,mirror_sync_delete,approve_pull_request,reject_pull_request,comment_pull,publish_release,pull_review_dismissed,pull_request_ready_for_review,auto_merge_pull_request 11 14 OpType string `json:"op_type"` 12 15 ActUserID int64 `json:"act_user_id"` 13 16 ActUser *User `json:"act_user"`
+44
options/gitignore/Alteryx
··· 1 + # gitignore template for Alteryx Designer 2 + # website: https://www.alteryx.com/ 3 + # website: https://help.alteryx.com/current/designer/alteryx-file-types 4 + 5 + # Alteryx Data Files 6 + *.yxdb 7 + *.cydb 8 + *.cyidx 9 + *.rptx 10 + *.vvf 11 + *.aws 12 + 13 + # Alteryx Special Files 14 + *.yxwv 15 + *.yxft 16 + *.yxbe 17 + *.bak 18 + *.pcxml 19 + *.log 20 + *.bin 21 + *.yxlang 22 + CASS.ini 23 + 24 + # Alteryx License Files 25 + *.yxlc 26 + *.slc 27 + *.cylc 28 + *.alc 29 + *.gzlc 30 + 31 + ## gitignore reference sites 32 + # https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#Ignoring-Files 33 + # https://git-scm.com/docs/gitignore 34 + # https://help.github.com/articles/ignoring-files/ 35 + 36 + ## Useful knowledge from stackoverflow 37 + # Even if you haven't tracked the files so far, git seems to be able to "know" about them even after you add them to .gitignore. 38 + # WARNING: First commit your current changes, or you will lose them. 39 + # Then run the following commands from the top folder of your git repo: 40 + # git rm -r --cached . 41 + # git add . 42 + # git commit -m "fixed untracked files" 43 + 44 + # author: Kacper Ksieski
+2
options/gitignore/Archives
··· 14 14 *.lzma 15 15 *.cab 16 16 *.xar 17 + *.zst 18 + *.tzst 17 19 18 20 # Packing-only formats 19 21 *.iso
+11
options/gitignore/Ballerina
··· 1 + # generated files 2 + target/ 3 + generated/ 4 + 5 + # dependencies 6 + Dependencies.toml 7 + 8 + # config files 9 + Config.toml 10 + # the config files used for testing, Uncomment the following line if you want to commit the test config files 11 + #!**/tests/Config.toml
+1
options/gitignore/CMake
··· 9 9 compile_commands.json 10 10 CTestTestfile.cmake 11 11 _deps 12 + CMakeUserPresets.json
+12
options/gitignore/Delphi
··· 26 26 #*.obj 27 27 # 28 28 29 + # Default Delphi compiler directories 30 + # Content of this directories are generated with each Compile/Construct of a project. 31 + # Most of the time, files here have not there place in a code repository. 32 + #Win32/ 33 + #Win64/ 34 + #OSX64/ 35 + #OSXARM64/ 36 + #Android/ 37 + #Android64/ 38 + #iOSDevice64/ 39 + #Linux64/ 40 + 29 41 # Delphi compiler-generated binaries (safe to delete) 30 42 *.exe 31 43 *.dll
+18
options/gitignore/GitHubPages
··· 1 + # This .gitignore is appropriate for repositories deployed to GitHub Pages and using 2 + # a Gemfile as specified at https://github.com/github/pages-gem#conventional 3 + 4 + # Basic Jekyll gitignores (synchronize to Jekyll.gitignore) 5 + _site/ 6 + .sass-cache/ 7 + .jekyll-cache/ 8 + .jekyll-metadata 9 + 10 + # Additional Ruby/bundler ignore for when you run: bundle install 11 + /vendor 12 + 13 + # Specific ignore for GitHub Pages 14 + # GitHub Pages will always use its own deployed version of pages-gem 15 + # This means GitHub Pages will NOT use your Gemfile.lock and therefore it is 16 + # counterproductive to check this file into the repository. 17 + # Details at https://github.com/github/pages-gem/issues/768 18 + Gemfile.lock
+3
options/gitignore/Go
··· 20 20 # Go workspace file 21 21 go.work 22 22 go.work.sum 23 + 24 + # env file 25 + .env
-17
options/gitignore/Objective-C
··· 5 5 ## User settings 6 6 xcuserdata/ 7 7 8 - ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 - *.xcscmblueprint 10 - *.xccheckout 11 - 12 - ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 - build/ 14 - DerivedData/ 15 - *.moved-aside 16 - *.pbxuser 17 - !default.pbxuser 18 - *.mode1v3 19 - !default.mode1v3 20 - *.mode2v3 21 - !default.mode2v3 22 - *.perspectivev3 23 - !default.perspectivev3 24 - 25 8 ## Obj-C/Swift specific 26 9 *.hmap 27 10
+7
options/gitignore/Rust
··· 12 12 13 13 # MSVC Windows builds of rustc generate these, which store debugging information 14 14 *.pdb 15 + 16 + # RustRover 17 + # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 + # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 + # and can be added to the global gitignore or merged into this file. For a more nuclear 20 + # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 + #.idea/
-28
options/gitignore/Swift
··· 5 5 ## User settings 6 6 xcuserdata/ 7 7 8 - ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 - *.xcscmblueprint 10 - *.xccheckout 11 - 12 - ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 - build/ 14 - DerivedData/ 15 - *.moved-aside 16 - *.pbxuser 17 - !default.pbxuser 18 - *.mode1v3 19 - !default.mode1v3 20 - *.mode2v3 21 - !default.mode2v3 22 - *.perspectivev3 23 - !default.perspectivev3 24 - 25 8 ## Obj-C/Swift specific 26 9 *.hmap 27 10 ··· 66 49 67 50 Carthage/Build/ 68 51 69 - # Accio dependency management 70 - Dependencies/ 71 - .accio/ 72 - 73 52 # fastlane 74 53 # 75 54 # It is recommended to not store the screenshots in the git repo. ··· 81 60 fastlane/Preview.html 82 61 fastlane/screenshots/**/*.png 83 62 fastlane/test_output 84 - 85 - # Code Injection 86 - # 87 - # After new code Injection tools there's a generated folder /iOSInjectionProject 88 - # https://github.com/johnno1962/injectionforxcode 89 - 90 - iOSInjectionProject/
+5
options/gitignore/TeX
··· 39 39 *.synctex.gz 40 40 *.synctex.gz(busy) 41 41 *.pdfsync 42 + *.rubbercache 43 + rubber.cache 42 44 43 45 ## Build tool directories for auxiliary files 44 46 # latexrun ··· 137 139 *.lg 138 140 *.trc 139 141 *.xref 142 + 143 + # hypdoc 144 + *.hd 140 145 141 146 # hyperref 142 147 *.brf
+6
options/gitignore/Terraform
··· 23 23 *_override.tf 24 24 *_override.tf.json 25 25 26 + # Ignore transient lock info files created by terraform apply 27 + .terraform.tfstate.lock.info 28 + 26 29 # Include override files you do wish to add to version control using negated pattern 27 30 # !example_override.tf 28 31 ··· 32 35 # Ignore CLI configuration files 33 36 .terraformrc 34 37 terraform.rc 38 + 39 + # Ignore hcl file 40 + .terraform.lock.hcl
+11
options/gitignore/UiPath
··· 1 + # gitignore template for RPA development using UiPath Studio 2 + # website: https://www.uipath.com/product/studio 3 + # 4 + # Recommended: n/a 5 + 6 + # Ignore folders that could cause issues if accidentally tracked 7 + **/.local/** 8 + **/.settings/** 9 + **/.objects/** 10 + **/.tmh/** 11 + **/*.log
+2 -2
options/gitignore/UnrealEngine
··· 47 47 48 48 # Binary Files 49 49 Binaries/* 50 - Plugins/*/Binaries/* 50 + Plugins/**/Binaries/* 51 51 52 52 # Builds 53 53 Build/* ··· 68 68 69 69 # Compiled source files for the engine to use 70 70 Intermediate/* 71 - Plugins/*/Intermediate/* 71 + Plugins/**/Intermediate/* 72 72 73 73 # Cache files for the editor to use 74 74 DerivedDataCache/*
-4
options/gitignore/Xcode
··· 1 1 ## User settings 2 2 xcuserdata/ 3 - 4 - ## Xcode 8 and earlier 5 - *.xcscmblueprint 6 - *.xccheckout
+1 -4
pyproject.toml
··· 1 1 [tool.poetry] 2 - name = "forgejo" 3 - version = "0.0.0" 4 - description = "" 5 - authors = [] 2 + package-mode = false 6 3 7 4 [tool.poetry.dependencies] 8 5 python = "^3.10"
+3
release-notes/8.0.0/feat/4083.md
··· 1 + - add [Reviewed-on and Reviewed-by variables](https://codeberg.org/forgejo/forgejo/commit/4ddd9af50fbfcfb2ebf629697a803b3bce56c4af) to the merge template 2 + - [add the `[ui.csv].MAX_ROWS` setting](https://codeberg.org/forgejo/forgejo/commit/433b6c6910f8699dc41787ef8f5148b122b4677e) to avoid displaying a large number of lines (defaults to 2500) 3 + - [add a setting to override or add headers of all outgoing emails](https://codeberg.org/forgejo/forgejo/commit/1d4bff4f65d5e4a3969871ef91d3612daf272b45), for instance `Reply-To` or `In-Reply-To`
+1
release-notes/8.0.0/fix/4083.md
··· 1 + - [NuGet Package fails `choco info pkgname` when `pkgname` is also a substring of another package Id](https://codeberg.org/forgejo/forgejo/commit/c6e04c3c9eddfa6c4bec541f681c8d300b157cdb)
+29 -19
routers/api/packages/nuget/nuget.go
··· 96 96 xmlResponse(ctx, http.StatusOK, Metadata) 97 97 } 98 98 99 - var searchTermExtract = regexp.MustCompile(`'([^']+)'`) 99 + var ( 100 + searchTermExtract = regexp.MustCompile(`'([^']+)'`) 101 + searchTermExact = regexp.MustCompile(`\s+eq\s+'`) 102 + ) 100 103 101 - func getSearchTerm(ctx *context.Context) string { 104 + func getSearchTerm(ctx *context.Context) packages_model.SearchValue { 102 105 searchTerm := strings.Trim(ctx.FormTrim("searchTerm"), "'") 103 - if searchTerm == "" { 104 - // $filter contains a query like: 105 - // (((Id ne null) and substringof('microsoft',tolower(Id))) 106 - // We don't support these queries, just extract the search term. 107 - match := searchTermExtract.FindStringSubmatch(ctx.FormTrim("$filter")) 108 - if len(match) == 2 { 109 - searchTerm = strings.TrimSpace(match[1]) 106 + if searchTerm != "" { 107 + return packages_model.SearchValue{ 108 + Value: searchTerm, 109 + ExactMatch: false, 110 110 } 111 111 } 112 - return searchTerm 112 + 113 + // $filter contains a query like: 114 + // (((Id ne null) and substringof('microsoft',tolower(Id))) 115 + // https://www.odata.org/documentation/odata-version-2-0/uri-conventions/ section 4.5 116 + // We don't support these queries, just extract the search term. 117 + filter := ctx.FormTrim("$filter") 118 + match := searchTermExtract.FindStringSubmatch(filter) 119 + if len(match) == 2 { 120 + return packages_model.SearchValue{ 121 + Value: strings.TrimSpace(match[1]), 122 + ExactMatch: searchTermExact.MatchString(filter), 123 + } 124 + } 125 + 126 + return packages_model.SearchValue{} 113 127 } 114 128 115 129 // https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs ··· 118 132 paginator := db.NewAbsoluteListOptions(skip, take) 119 133 120 134 pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{ 121 - OwnerID: ctx.Package.Owner.ID, 122 - Type: packages_model.TypeNuGet, 123 - Name: packages_model.SearchValue{ 124 - Value: getSearchTerm(ctx), 125 - }, 135 + OwnerID: ctx.Package.Owner.ID, 136 + Type: packages_model.TypeNuGet, 137 + Name: getSearchTerm(ctx), 126 138 IsInternal: optional.Some(false), 127 139 Paginator: paginator, 128 140 }) ··· 169 181 // http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part2-url-conventions/odata-v4.0-errata03-os-part2-url-conventions-complete.html#_Toc453752351 170 182 func SearchServiceV2Count(ctx *context.Context) { 171 183 count, err := nuget_model.CountPackages(ctx, &packages_model.PackageSearchOptions{ 172 - OwnerID: ctx.Package.Owner.ID, 173 - Name: packages_model.SearchValue{ 174 - Value: getSearchTerm(ctx), 175 - }, 184 + OwnerID: ctx.Package.Owner.ID, 185 + Name: getSearchTerm(ctx), 176 186 IsInternal: optional.Some(false), 177 187 }) 178 188 if err != nil {
+1 -1
routers/web/repo/issue_content_history.go
··· 158 158 159 159 // use chroma to render the diff html 160 160 diffHTMLBuf := bytes.Buffer{} 161 - diffHTMLBuf.WriteString("<pre class='chroma' style='tab-size: 4'>") 161 + diffHTMLBuf.WriteString("<pre class='chroma'>") 162 162 for _, it := range diff { 163 163 if it.Type == diffmatchpatch.DiffInsert { 164 164 diffHTMLBuf.WriteString("<span class='gi'>")
+9 -1
services/mailer/mailer.go
··· 57 57 msg.SetHeader(header, m.Headers[header]...) 58 58 } 59 59 60 - if len(setting.MailService.SubjectPrefix) > 0 { 60 + if setting.MailService.SubjectPrefix != "" { 61 61 msg.SetHeader("Subject", setting.MailService.SubjectPrefix+" "+m.Subject) 62 62 } else { 63 63 msg.SetHeader("Subject", m.Subject) ··· 79 79 if len(msg.GetHeader("Message-ID")) == 0 { 80 80 msg.SetHeader("Message-ID", m.generateAutoMessageID()) 81 81 } 82 + 83 + for k, v := range setting.MailService.OverrideHeader { 84 + if len(msg.GetHeader(k)) != 0 { 85 + log.Debug("Mailer override header '%s' as per config", k) 86 + } 87 + msg.SetHeader(k, v...) 88 + } 89 + 82 90 return msg 83 91 } 84 92
+80 -6
services/mailer/mailer_test.go
··· 4 4 package mailer 5 5 6 6 import ( 7 + "strings" 7 8 "testing" 8 9 "time" 9 10 10 11 repo_model "code.gitea.io/gitea/models/repo" 11 12 "code.gitea.io/gitea/modules/setting" 13 + "code.gitea.io/gitea/modules/test" 12 14 13 15 "github.com/stretchr/testify/assert" 14 16 ) 15 17 16 18 func TestGenerateMessageID(t *testing.T) { 17 - mailService := setting.Mailer{ 19 + defer test.MockVariableValue(&setting.MailService, &setting.Mailer{ 18 20 From: "test@gitea.com", 19 - } 20 - 21 - setting.MailService = &mailService 22 - setting.Domain = "localhost" 21 + })() 22 + defer test.MockVariableValue(&setting.Domain, "localhost")() 23 23 24 24 date := time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC) 25 25 m := NewMessageFrom("", "display-name", "from-address", "subject", "body") ··· 39 39 } 40 40 41 41 func TestGenerateMessageIDForRelease(t *testing.T) { 42 - setting.Domain = "localhost" 42 + defer test.MockVariableValue(&setting.Domain, "localhost")() 43 43 44 44 rel := repo_model.Release{ 45 45 ID: 42, ··· 51 51 m := createMessageIDForRelease(&rel) 52 52 assert.Equal(t, "<test/tag-test/releases/42@localhost>", m) 53 53 } 54 + 55 + func TestToMessage(t *testing.T) { 56 + defer test.MockVariableValue(&setting.MailService, &setting.Mailer{ 57 + From: "test@gitea.com", 58 + })() 59 + defer test.MockVariableValue(&setting.Domain, "localhost")() 60 + 61 + m1 := Message{ 62 + Info: "info", 63 + FromAddress: "test@gitea.com", 64 + FromDisplayName: "Test Gitea", 65 + To: "a@b.com", 66 + Subject: "Issue X Closed", 67 + Body: "Some Issue got closed by Y-Man", 68 + } 69 + 70 + buf := &strings.Builder{} 71 + _, err := m1.ToMessage().WriteTo(buf) 72 + assert.NoError(t, err) 73 + header, _ := extractMailHeaderAndContent(t, buf.String()) 74 + assert.EqualValues(t, map[string]string{ 75 + "Content-Type": "multipart/alternative;", 76 + "Date": "Mon, 01 Jan 0001 00:00:00 +0000", 77 + "From": "\"Test Gitea\" <test@gitea.com>", 78 + "Message-ID": "<autogen--6795364578871-69c000786adc60dc@localhost>", 79 + "Mime-Version": "1.0", 80 + "Subject": "Issue X Closed", 81 + "To": "a@b.com", 82 + "X-Auto-Response-Suppress": "All", 83 + }, header) 84 + 85 + setting.MailService.OverrideHeader = map[string][]string{ 86 + "Message-ID": {""}, // delete message id 87 + "Auto-Submitted": {"auto-generated"}, // suppress auto replay 88 + } 89 + 90 + buf = &strings.Builder{} 91 + _, err = m1.ToMessage().WriteTo(buf) 92 + assert.NoError(t, err) 93 + header, _ = extractMailHeaderAndContent(t, buf.String()) 94 + assert.EqualValues(t, map[string]string{ 95 + "Content-Type": "multipart/alternative;", 96 + "Date": "Mon, 01 Jan 0001 00:00:00 +0000", 97 + "From": "\"Test Gitea\" <test@gitea.com>", 98 + "Message-ID": "", 99 + "Mime-Version": "1.0", 100 + "Subject": "Issue X Closed", 101 + "To": "a@b.com", 102 + "X-Auto-Response-Suppress": "All", 103 + "Auto-Submitted": "auto-generated", 104 + }, header) 105 + } 106 + 107 + func extractMailHeaderAndContent(t *testing.T, mail string) (map[string]string, string) { 108 + header := make(map[string]string) 109 + 110 + parts := strings.SplitN(mail, "boundary=", 2) 111 + if !assert.Len(t, parts, 2) { 112 + return nil, "" 113 + } 114 + content := strings.TrimSpace("boundary=" + parts[1]) 115 + 116 + hParts := strings.Split(parts[0], "\n") 117 + 118 + for _, hPart := range hParts { 119 + parts := strings.SplitN(hPart, ":", 2) 120 + hk := strings.TrimSpace(parts[0]) 121 + if hk != "" { 122 + header[hk] = strings.TrimSpace(parts[1]) 123 + } 124 + } 125 + 126 + return header, content 127 + }
+1 -1
services/migrations/migrate.go
··· 183 183 // migrateRepository will download information and then upload it to Uploader, this is a simple 184 184 // process for small repository. For a big repository, save all the data to disk 185 185 // before upload is better 186 - func migrateRepository(ctx context.Context, doer *user_model.User, downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error { 186 + func migrateRepository(_ context.Context, doer *user_model.User, downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error { 187 187 if messenger == nil { 188 188 messenger = base.NilMessenger 189 189 }
+14 -4
services/pull/merge.go
··· 46 46 if err := pr.Issue.LoadPoster(ctx); err != nil { 47 47 return "", "", err 48 48 } 49 + if err := pr.Issue.LoadRepo(ctx); err != nil { 50 + return "", "", err 51 + } 49 52 50 53 isExternalTracker := pr.BaseRepo.UnitEnabled(ctx, unit.TypeExternalTracker) 51 54 issueReference := "#" ··· 53 56 issueReference = "!" 54 57 } 55 58 59 + reviewedOn := fmt.Sprintf("Reviewed-on: %s/%s", setting.AppURL, pr.Issue.Link()) 60 + reviewedBy := pr.GetApprovers(ctx) 61 + 56 62 if mergeStyle != "" { 57 63 commit, err := baseGitRepo.GetBranchCommit(pr.BaseRepo.DefaultBranch) 58 64 if err != nil { ··· 83 89 "PullRequestPosterName": pr.Issue.Poster.Name, 84 90 "PullRequestIndex": strconv.FormatInt(pr.Index, 10), 85 91 "PullRequestReference": fmt.Sprintf("%s%d", issueReference, pr.Index), 92 + "ReviewedOn": reviewedOn, 93 + "ReviewedBy": reviewedBy, 86 94 } 87 95 if pr.HeadRepo != nil { 88 96 vars["HeadRepoOwnerName"] = pr.HeadRepo.OwnerName ··· 122 130 return "", "", nil 123 131 } 124 132 133 + body = fmt.Sprintf("%s\n%s", reviewedOn, reviewedBy) 134 + 125 135 // Squash merge has a different from other styles. 126 136 if mergeStyle == repo_model.MergeStyleSquash { 127 - return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), "", nil 137 + return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), body, nil 128 138 } 129 139 130 140 if pr.BaseRepoID == pr.HeadRepoID { 131 - return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch), "", nil 141 + return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch), body, nil 132 142 } 133 143 134 144 if pr.HeadRepo == nil { 135 - return fmt.Sprintf("Merge pull request '%s' (%s%d) from <deleted>:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch), "", nil 145 + return fmt.Sprintf("Merge pull request '%s' (%s%d) from <deleted>:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch), body, nil 136 146 } 137 147 138 - return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch), "", nil 148 + return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch), body, nil 139 149 } 140 150 141 151 func expandDefaultMergeMessage(template string, vars map[string]string) (message, body string) {
+2 -2
services/repository/transfer.go
··· 285 285 } 286 286 287 287 // changeRepositoryName changes all corresponding setting from old repository name to new one. 288 - func changeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, newRepoName string) (err error) { 288 + func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newRepoName string) (err error) { 289 289 oldRepoName := repo.Name 290 290 newRepoName = strings.ToLower(newRepoName) 291 291 if err = repo_model.IsUsableRepoName(newRepoName); err != nil { ··· 347 347 // local copy's origin accordingly. 348 348 349 349 repoWorkingPool.CheckIn(fmt.Sprint(repo.ID)) 350 - if err := changeRepositoryName(ctx, doer, repo, newRepoName); err != nil { 350 + if err := changeRepositoryName(ctx, repo, newRepoName); err != nil { 351 351 repoWorkingPool.CheckOut(fmt.Sprint(repo.ID)) 352 352 return err 353 353 }
+23
tailwind.config.js
··· 1 1 import {readFileSync} from 'node:fs'; 2 2 import {env} from 'node:process'; 3 3 import {parse} from 'postcss'; 4 + import plugin from 'tailwindcss/plugin.js'; 4 5 5 6 const isProduction = env.NODE_ENV !== 'development'; 6 7 ··· 98 99 })), 99 100 }, 100 101 }, 102 + plugins: [ 103 + plugin(({addUtilities}) => { 104 + addUtilities({ 105 + // tw-hidden must win all other "display: xxx !important" classes to get the chance to "hide" an element. 106 + // do not use: 107 + // * "[hidden]" attribute: it's too weak, can not be applied to an element with "display: flex" 108 + // * ".hidden" class: it has been polluted by Fomantic UI in many cases 109 + // * inline style="display: none": it's difficult to tweak 110 + // * jQuery's show/hide/toggle: it can not show/hide elements with "display: xxx !important" 111 + // only use: 112 + // * this ".tw-hidden" class 113 + // * showElem/hideElem/toggleElem functions in "utils/dom.js" 114 + '.hidden.hidden': { 115 + 'display': 'none', 116 + }, 117 + // proposed class from https://github.com/tailwindlabs/tailwindcss/pull/12128 118 + '.break-anywhere': { 119 + 'overflow-wrap': 'anywhere', 120 + }, 121 + }); 122 + }), 123 + ], 101 124 };
+1 -1
templates/admin/packages/list.tmpl
··· 5 5 {{ctx.Locale.Tr "admin.packages.total_size" (ctx.Locale.TrSize .TotalBlobSize)}}, 6 6 {{ctx.Locale.Tr "admin.packages.unreferenced_size" (ctx.Locale.TrSize .TotalUnreferencedBlobSize)}}) 7 7 <div class="ui right"> 8 - <form method="post" action="/admin/packages/cleanup"> 8 + <form method="post" action="{{AppSubUrl}}/admin/packages/cleanup"> 9 9 {{.CsrfTokenHtml}} 10 10 <button class="ui primary tiny button">{{ctx.Locale.Tr "admin.packages.cleanup"}}</button> 11 11 </form>
+1 -1
templates/devtest/fetch-action.tmpl
··· 25 25 <div><label><input name="check" type="checkbox"> check</label></div> 26 26 <div><button name="btn">submit post</button></div> 27 27 </form> 28 - <form method="post" action="/no-such-uri" class="form-fetch-action"> 28 + <form method="post" action="no-such-uri" class="form-fetch-action"> 29 29 <div class="tw-py-8">bad action url</div> 30 30 <div><button name="btn">submit test</button></div> 31 31 </form>
+1 -1
templates/projects/view.tmpl
··· 66 66 <div id="project-board"> 67 67 <div class="board {{if .CanWriteProjects}}sortable{{end}}"{{if .CanWriteProjects}} data-url="{{$.Link}}/move"{{end}}> 68 68 {{range .Columns}} 69 - <div class="ui segment project-column"{{if .Color}} style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}"> 69 + <div class="project-column"{{if .Color}} style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}"> 70 70 <div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}"> 71 71 <div class="ui large label project-column-title tw-py-1"> 72 72 <div class="ui small circular grey label project-column-issue-count">
+2 -2
templates/repo/code/recently_pushed_new_branches.tmpl
··· 1 1 {{range .RecentlyPushedNewBranches}} 2 - <div class="ui positive message tw-flex tw-items-center"> 3 - <div class="tw-flex-1"> 2 + <div class="ui positive message tw-flex tw-items-center tw-gap-2"> 3 + <div class="tw-flex-1 tw-break-anywhere"> 4 4 {{$timeSince := TimeSince .CommitTime.AsTime ctx.Locale}} 5 5 {{$repo := .GetRepo $.Context}} 6 6 {{$name := .Name}}
+1 -1
templates/repo/issue/card.tmpl
··· 14 14 <div class="issue-card-icon"> 15 15 {{template "shared/issueicon" .}} 16 16 </div> 17 - <a class="issue-card-title muted issue-title" href="{{.Link}}">{{.Title | RenderEmoji ctx | RenderCodeBlock}}</a> 17 + <a class="issue-card-title muted issue-title tw-break-anywhere" href="{{.Link}}">{{.Title | RenderEmoji ctx | RenderCodeBlock}}</a> 18 18 {{if and $.isPinnedIssueCard $.Page.IsRepoAdmin}} 19 19 <a role="button" class="issue-card-unpin muted tw-flex tw-items-center" data-tooltip-content={{ctx.Locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Page.Link}}/unpin/{{.Index}}"> 20 20 {{svg "octicon-x" 16}}
+2 -4
templates/repo/issue/view_content/pull.tmpl
··· 199 199 200 200 {{if .AllowMerge}} {{/* user is allowed to merge */}} 201 201 {{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}} 202 - {{$approvers := (.Issue.PullRequest.GetApprovers ctx)}} 203 202 {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}} 204 203 {{$hasPendingPullRequestMergeTip := ""}} 205 204 {{if .HasPendingPullRequestMerge}} ··· 208 207 {{end}} 209 208 <div class="divider"></div> 210 209 <script type="module"> 211 - const issueUrl = window.location.origin + {{$.Issue.Link}}; 212 210 const defaultMergeTitle = {{.DefaultMergeMessage}}; 213 211 const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}}; 214 - const defaultMergeMessage = {{if .DefaultMergeBody}}{{.DefaultMergeBody}}{{else}}`Reviewed-on: ${issueUrl}\n` + {{$approvers}}{{end}}; 215 - const defaultSquashMergeMessage = {{if .DefaultSquashMergeBody}}{{.DefaultSquashMergeBody}}{{else}}`Reviewed-on: ${issueUrl}\n` + {{$approvers}}{{end}}; 212 + const defaultMergeMessage = {{.DefaultMergeBody}}; 213 + const defaultSquashMergeMessage = {{.DefaultSquashMergeBody}}; 216 214 const mergeForm = { 217 215 'baseLink': {{.Link}}, 218 216 'textCancel': {{ctx.Locale.Tr "cancel"}},
+1 -1
templates/shared/actions/runner_list.tmpl
··· 70 70 <td><p data-tooltip-content="{{.Description}}">{{.Name}}</p></td> 71 71 <td>{{if .Version}}{{.Version}}{{else}}{{ctx.Locale.Tr "unknown"}}{{end}}</td> 72 72 <td><span data-tooltip-content="{{.BelongsToOwnerName}}">{{.BelongsToOwnerType.LocaleString ctx.Locale}}</span></td> 73 - <td class="runner-tags"> 73 + <td class="tw-flex tw-flex-wrap tw-gap-2 runner-tags"> 74 74 {{range .AgentLabels}}<span class="ui label">{{.}}</span>{{end}} 75 75 </td> 76 76 <td>{{if .LastOnline}}{{TimeSinceUnix .LastOnline ctx.Locale}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</td>
+30
templates/swagger/v1_json.tmpl
··· 18537 18537 "x-go-name": "IsPrivate" 18538 18538 }, 18539 18539 "op_type": { 18540 + "description": "the type of action", 18540 18541 "type": "string", 18542 + "enum": [ 18543 + "create_repo", 18544 + "rename_repo", 18545 + "star_repo", 18546 + "watch_repo", 18547 + "commit_repo", 18548 + "create_issue", 18549 + "create_pull_request", 18550 + "transfer_repo", 18551 + "push_tag", 18552 + "comment_issue", 18553 + "merge_pull_request", 18554 + "close_issue", 18555 + "reopen_issue", 18556 + "close_pull_request", 18557 + "reopen_pull_request", 18558 + "delete_tag", 18559 + "delete_branch", 18560 + "mirror_sync_push", 18561 + "mirror_sync_create", 18562 + "mirror_sync_delete", 18563 + "approve_pull_request", 18564 + "reject_pull_request", 18565 + "comment_pull", 18566 + "publish_release", 18567 + "pull_review_dismissed", 18568 + "pull_request_ready_for_review", 18569 + "auto_merge_pull_request" 18570 + ], 18541 18571 "x-go-name": "OpType" 18542 18572 }, 18543 18573 "ref_name": {
+2 -2
templates/user/notification/notification_div.tmpl
··· 49 49 {{end}} 50 50 </div> 51 51 <a class="notifications-link tw-flex tw-flex-1 tw-flex-col silenced" href="{{.Link ctx}}"> 52 - <div class="notifications-top-row tw-text-13"> 52 + <div class="notifications-top-row tw-text-13 tw-break-anywhere"> 53 53 {{.Repository.FullName}} {{if .Issue}}<span class="text light-3">#{{.Issue.Index}}</span>{{end}} 54 54 {{if eq .Status 3}} 55 55 {{svg "octicon-pin" 13 "text blue tw-mt-0.5 tw-ml-1"}} 56 56 {{end}} 57 57 </div> 58 58 <div class="notifications-bottom-row tw-text-16 tw-py-0.5"> 59 - <span class="issue-title"> 59 + <span class="issue-title tw-break-anywhere"> 60 60 {{if .Issue}} 61 61 {{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}} 62 62 {{else}}
+1 -1
templates/user/settings/applications.tmpl
··· 75 75 {{ctx.Locale.Tr "settings.select_permissions"}} 76 76 </summary> 77 77 <p class="activity meta"> 78 - <p>{{ctx.Locale.Tr "settings.access_token_desc" (`href="/api/swagger" target="_blank"`|SafeHTML) (`href="https://forgejo.org/docs/latest/user/token-scope/" target="_blank"`|SafeHTML)}}</p> 78 + <p>{{ctx.Locale.Tr "settings.access_token_desc" (HTMLFormat `href="%s/api/swagger" target="_blank"` AppSubUrl) (`href="https://forgejo.org/docs/latest/user/token-scope/" target="_blank"`|SafeHTML)}}</p> 79 79 </p> 80 80 <div class="scoped-access-token-mount"> 81 81 <scoped-access-token-selector
+86 -16
tests/integration/api_packages_nuget_test.go
··· 431 431 432 432 t.Run("SearchService", func(t *testing.T) { 433 433 cases := []struct { 434 - Query string 435 - Skip int 436 - Take int 437 - ExpectedTotal int64 438 - ExpectedResults int 434 + Query string 435 + Skip int 436 + Take int 437 + ExpectedTotal int64 438 + ExpectedResults int 439 + ExpectedExactMatch bool 439 440 }{ 440 - {"", 0, 0, 1, 1}, 441 - {"", 0, 10, 1, 1}, 442 - {"gitea", 0, 10, 0, 0}, 443 - {"test", 0, 10, 1, 1}, 444 - {"test", 1, 10, 1, 0}, 441 + {"", 0, 0, 4, 4, false}, 442 + {"", 0, 10, 4, 4, false}, 443 + {"gitea", 0, 10, 0, 0, false}, 444 + {"test", 0, 10, 1, 1, false}, 445 + {"test", 1, 10, 1, 0, false}, 446 + {"almost.similar", 0, 0, 3, 3, true}, 447 + } 448 + 449 + fakePackages := []string{ 450 + packageName, 451 + "almost.similar.dependency", 452 + "almost.similar", 453 + "almost.similar.dependant", 445 454 } 446 455 447 - req := NewRequestWithBody(t, "PUT", url, createPackage(packageName, "1.0.99")). 448 - AddBasicAuth(user.Name) 449 - MakeRequest(t, req, http.StatusCreated) 456 + for _, fakePackageName := range fakePackages { 457 + req := NewRequestWithBody(t, "PUT", url, createPackage(fakePackageName, "1.0.99")). 458 + AddBasicAuth(user.Name) 459 + MakeRequest(t, req, http.StatusCreated) 460 + } 450 461 451 462 t.Run("v2", func(t *testing.T) { 452 463 t.Run("Search()", func(t *testing.T) { ··· 493 504 } 494 505 }) 495 506 507 + t.Run("Packages()", func(t *testing.T) { 508 + defer tests.PrintCurrentTest(t)() 509 + 510 + t.Run("substringof", func(t *testing.T) { 511 + defer tests.PrintCurrentTest(t)() 512 + 513 + for i, c := range cases { 514 + req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages()?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 515 + AddBasicAuth(user.Name) 516 + resp := MakeRequest(t, req, http.StatusOK) 517 + 518 + var result FeedResponse 519 + decodeXML(t, resp, &result) 520 + 521 + assert.Equal(t, c.ExpectedTotal, result.Count, "case %d: unexpected total hits", i) 522 + assert.Len(t, result.Entries, c.ExpectedResults, "case %d: unexpected result count", i) 523 + 524 + req = NewRequest(t, "GET", fmt.Sprintf("%s/Packages()/$count?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 525 + AddBasicAuth(user.Name) 526 + resp = MakeRequest(t, req, http.StatusOK) 527 + 528 + assert.Equal(t, strconv.FormatInt(c.ExpectedTotal, 10), resp.Body.String(), "case %d: unexpected total hits", i) 529 + } 530 + }) 531 + 532 + t.Run("IdEq", func(t *testing.T) { 533 + defer tests.PrintCurrentTest(t)() 534 + 535 + for i, c := range cases { 536 + if c.Query == "" { 537 + // Ignore the `tolower(Id) eq ''` as it's unlikely to happen 538 + continue 539 + } 540 + req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages()?$filter=(tolower(Id) eq '%s')&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 541 + AddBasicAuth(user.Name) 542 + resp := MakeRequest(t, req, http.StatusOK) 543 + 544 + var result FeedResponse 545 + decodeXML(t, resp, &result) 546 + 547 + expectedCount := 0 548 + if c.ExpectedExactMatch { 549 + expectedCount = 1 550 + } 551 + 552 + assert.Equal(t, int64(expectedCount), result.Count, "case %d: unexpected total hits", i) 553 + assert.Len(t, result.Entries, expectedCount, "case %d: unexpected result count", i) 554 + 555 + req = NewRequest(t, "GET", fmt.Sprintf("%s/Packages()/$count?$filter=(tolower(Id) eq '%s')&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)). 556 + AddBasicAuth(user.Name) 557 + resp = MakeRequest(t, req, http.StatusOK) 558 + 559 + assert.Equal(t, strconv.FormatInt(int64(expectedCount), 10), resp.Body.String(), "case %d: unexpected total hits", i) 560 + } 561 + }) 562 + }) 563 + 496 564 t.Run("Next", func(t *testing.T) { 497 565 req := NewRequest(t, "GET", fmt.Sprintf("%s/Search()?searchTerm='test'&$skip=0&$top=1", url)). 498 566 AddBasicAuth(user.Name) ··· 550 618 }) 551 619 }) 552 620 553 - req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, "1.0.99")). 554 - AddBasicAuth(user.Name) 555 - MakeRequest(t, req, http.StatusNoContent) 621 + for _, fakePackageName := range fakePackages { 622 + req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, fakePackageName, "1.0.99")). 623 + AddBasicAuth(user.Name) 624 + MakeRequest(t, req, http.StatusNoContent) 625 + } 556 626 }) 557 627 558 628 t.Run("RegistrationService", func(t *testing.T) {
+3 -3
tests/integration/api_repo_tags_test.go
··· 42 42 assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.zip", tags[0].ZipballURL) 43 43 assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.tar.gz", tags[0].TarballURL) 44 44 45 - newTag := createNewTagUsingAPI(t, session, token, user.Name, repoName, "gitea/22", "", "nice!\nand some text") 45 + newTag := createNewTagUsingAPI(t, token, user.Name, repoName, "gitea/22", "", "nice!\nand some text") 46 46 resp = MakeRequest(t, req, http.StatusOK) 47 47 DecodeJSON(t, resp, &tags) 48 48 assert.Len(t, tags, 2) ··· 72 72 MakeRequest(t, req, http.StatusNotFound) 73 73 } 74 74 75 - func createNewTagUsingAPI(t *testing.T, session *TestSession, token, ownerName, repoName, name, target, msg string) *api.Tag { 75 + func createNewTagUsingAPI(t *testing.T, token, ownerName, repoName, name, target, msg string) *api.Tag { 76 76 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags", ownerName, repoName) 77 77 req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateTagOption{ 78 78 TagName: name, ··· 96 96 repoName := "repo1" 97 97 tagName := "TagDownloadCount" 98 98 99 - createNewTagUsingAPI(t, session, token, user.Name, repoName, tagName, "", "") 99 + createNewTagUsingAPI(t, token, user.Name, repoName, tagName, "", "") 100 100 101 101 urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/%s?token=%s", user.Name, repoName, tagName, token) 102 102
+1 -1
tests/integration/dump_restore_test.go
··· 237 237 // 238 238 // Given []Something{} create afterPtr, beforePtr []*Something{} 239 239 // 240 - sliceType := reflect.SliceOf(reflect.PtrTo(t.Elem())) 240 + sliceType := reflect.SliceOf(reflect.PointerTo(t.Elem())) 241 241 beforeSlice := reflect.MakeSlice(sliceType, 0, 10) 242 242 beforePtr = reflect.New(beforeSlice.Type()) 243 243 beforePtr.Elem().Set(beforeSlice)
+3 -3
tests/integration/gpg_git_test.go
··· 36 36 defer os.Setenv("GNUPGHOME", oldGNUPGHome) 37 37 38 38 // Need to create a root key 39 - rootKeyPair, err := importTestingKey(tmpDir, "gitea", "gitea@fake.local") 39 + rootKeyPair, err := importTestingKey() 40 40 if !assert.NoError(t, err, "importTestingKey") { 41 41 return 42 42 } ··· 263 263 }) 264 264 } 265 265 266 - func crudActionCreateFile(t *testing.T, ctx APITestContext, user *user_model.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) { 266 + func crudActionCreateFile(_ *testing.T, ctx APITestContext, user *user_model.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) { 267 267 return doAPICreateFile(ctx, path, &api.CreateFileOptions{ 268 268 FileOptions: api.FileOptions{ 269 269 BranchName: from, ··· 282 282 }, callback...) 283 283 } 284 284 285 - func importTestingKey(tmpDir, name, email string) (*openpgp.Entity, error) { 285 + func importTestingKey() (*openpgp.Entity, error) { 286 286 if _, _, err := process.GetManager().Exec("gpg --import tests/integration/private-testing.key", "gpg", "--import", "tests/integration/private-testing.key"); err != nil { 287 287 return nil, err 288 288 }
+23
tools/lint-go-gopls.sh
··· 1 + #!/bin/bash 2 + set -uo pipefail 3 + 4 + cd "$(dirname -- "${BASH_SOURCE[0]}")" && cd .. 5 + 6 + IGNORE_PATTERNS=( 7 + "is deprecated" # TODO: fix these 8 + ) 9 + 10 + # lint all go files with 'gopls check' and look for lines starting with the 11 + # current absolute path, indicating a error was found. This is neccessary 12 + # because the tool does not set non-zero exit code when errors are found. 13 + # ref: https://github.com/golang/go/issues/67078 14 + ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}")); 15 + NUM_ERRORS=$(echo -n "$ERROR_LINES" | wc -l) 16 + 17 + if [ "$NUM_ERRORS" -eq "0" ]; then 18 + exit 0; 19 + else 20 + echo "$ERROR_LINES" 21 + echo "Found $NUM_ERRORS 'gopls check' errors" 22 + exit 1; 23 + fi
+1
web_src/css/features/projects.css
··· 9 9 .project-column { 10 10 background-color: var(--color-project-column-bg) !important; 11 11 border: 1px solid var(--color-secondary) !important; 12 + border-radius: var(--border-radius); 12 13 margin: 0 0.5rem !important; 13 14 padding: 0.5rem !important; 14 15 width: 320px;
-16
web_src/css/helpers.css
··· 35 35 .interact-bg:hover { background: var(--color-hover) !important; } 36 36 .interact-bg:active { background: var(--color-active) !important; } 37 37 38 - /* 39 - tw-hidden must win all other "display: xxx !important" classes to get the chance to "hide" an element. 40 - do not use: 41 - * "[hidden]" attribute: it's too weak, can not be applied to an element with "display: flex" 42 - * ".hidden" class: it has been polluted by Fomantic UI in many cases 43 - * inline style="display: none": it's difficult to tweak 44 - * jQuery's show/hide/toggle: it can not show/hide elements with "display: xxx !important" 45 - only use: 46 - * this ".tw-hidden" class 47 - * showElem/hideElem/toggleElem functions in "utils/dom.js" 48 - */ 49 - .tw-hidden.tw-hidden { display: none !important; } 50 - 51 - /* proposed class from https://github.com/tailwindlabs/tailwindcss/pull/12128 */ 52 - .tw-break-anywhere { overflow-wrap: anywhere !important; } 53 - 54 38 @media (max-width: 767.98px) { 55 39 /* double selector so it wins over .tw-flex (old .gt-df) etc */ 56 40 .not-mobile.not-mobile {
+1
web_src/css/repo.css
··· 2489 2489 min-height: 12em; 2490 2490 max-height: calc(100vh - 10.5rem); 2491 2491 overflow-y: auto; 2492 + tab-size: 4; 2492 2493 } 2493 2494 2494 2495 .comment-diff-data pre {