mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-21 08:31:27 -05:00
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>
This commit is contained in:
commit
df373c9f7e
53 changed files with 532 additions and 226 deletions
|
@ -10,7 +10,8 @@
|
||||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
|
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
|
||||||
"ghcr.io/devcontainers/features/python:1": {
|
"ghcr.io/devcontainers/features/python:1": {
|
||||||
"version": "3.12"
|
"version": "3.12"
|
||||||
}
|
},
|
||||||
|
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
||||||
},
|
},
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
|
@ -25,8 +26,9 @@
|
||||||
"Vue.volar",
|
"Vue.volar",
|
||||||
"ms-azuretools.vscode-docker",
|
"ms-azuretools.vscode-docker",
|
||||||
"vitest.explorer",
|
"vitest.explorer",
|
||||||
"qwtel.sqlite-viewer",
|
"cweijan.vscode-database-client2",
|
||||||
"GitHub.vscode-pull-request-github"
|
"GitHub.vscode-pull-request-github",
|
||||||
|
"Azurite.azurite"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,7 +43,7 @@ vscode:
|
||||||
- Vue.volar
|
- Vue.volar
|
||||||
- ms-azuretools.vscode-docker
|
- ms-azuretools.vscode-docker
|
||||||
- vitest.explorer
|
- vitest.explorer
|
||||||
- qwtel.sqlite-viewer
|
- cweijan.vscode-database-client2
|
||||||
- GitHub.vscode-pull-request-github
|
- GitHub.vscode-pull-request-github
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
|
|
10
Makefile
10
Makefile
|
@ -38,6 +38,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour
|
||||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
|
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
|
||||||
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.22.0 # renovate: datasource=go
|
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.22.0 # renovate: datasource=go
|
||||||
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go
|
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go
|
||||||
|
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.15.3 # renovate: datasource=go
|
||||||
|
|
||||||
DOCKER_IMAGE ?= gitea/gitea
|
DOCKER_IMAGE ?= gitea/gitea
|
||||||
DOCKER_TAG ?= latest
|
DOCKER_TAG ?= latest
|
||||||
|
@ -228,6 +229,7 @@ help:
|
||||||
@echo " - lint-go lint go files"
|
@echo " - lint-go lint go files"
|
||||||
@echo " - lint-go-fix lint go files and fix issues"
|
@echo " - lint-go-fix lint go files and fix issues"
|
||||||
@echo " - lint-go-vet lint go files with vet"
|
@echo " - lint-go-vet lint go files with vet"
|
||||||
|
@echo " - lint-go-gopls lint go files with gopls"
|
||||||
@echo " - lint-js lint js files"
|
@echo " - lint-js lint js files"
|
||||||
@echo " - lint-js-fix lint js files and fix issues"
|
@echo " - lint-js-fix lint js files and fix issues"
|
||||||
@echo " - lint-css lint css files"
|
@echo " - lint-css lint css files"
|
||||||
|
@ -468,6 +470,11 @@ lint-go-vet:
|
||||||
@echo "Running go vet..."
|
@echo "Running go vet..."
|
||||||
@$(GO) vet ./...
|
@$(GO) vet ./...
|
||||||
|
|
||||||
|
.PHONY: lint-go-gopls
|
||||||
|
lint-go-gopls:
|
||||||
|
@echo "Running gopls check..."
|
||||||
|
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
|
||||||
|
|
||||||
.PHONY: lint-editorconfig
|
.PHONY: lint-editorconfig
|
||||||
lint-editorconfig:
|
lint-editorconfig:
|
||||||
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .forgejo/workflows
|
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .forgejo/workflows
|
||||||
|
@ -879,13 +886,14 @@ deps-tools:
|
||||||
$(GO) install $(GO_LICENSES_PACKAGE)
|
$(GO) install $(GO_LICENSES_PACKAGE)
|
||||||
$(GO) install $(GOVULNCHECK_PACKAGE)
|
$(GO) install $(GOVULNCHECK_PACKAGE)
|
||||||
$(GO) install $(GOMOCK_PACKAGE)
|
$(GO) install $(GOMOCK_PACKAGE)
|
||||||
|
$(GO) install $(GOPLS_PACKAGE)
|
||||||
|
|
||||||
node_modules: package-lock.json
|
node_modules: package-lock.json
|
||||||
npm install --no-save
|
npm install --no-save
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
|
|
||||||
.venv: poetry.lock
|
.venv: poetry.lock
|
||||||
poetry install --no-root
|
poetry install
|
||||||
@touch .venv
|
@touch .venv
|
||||||
|
|
||||||
.PHONY: fomantic
|
.PHONY: fomantic
|
||||||
|
|
|
@ -1387,6 +1387,9 @@ LEVEL = Info
|
||||||
;;
|
;;
|
||||||
;; Maximum allowed file size in bytes to render CSV files as table. (Set to 0 for no limit).
|
;; Maximum allowed file size in bytes to render CSV files as table. (Set to 0 for no limit).
|
||||||
;MAX_FILE_SIZE = 524288
|
;MAX_FILE_SIZE = 524288
|
||||||
|
;;
|
||||||
|
;; Maximum allowed rows to render CSV files. (Set to 0 for no limit)
|
||||||
|
;MAX_ROWS = 2500
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -1746,6 +1749,16 @@ LEVEL = Info
|
||||||
;; convert \r\n to \n for Sendmail
|
;; convert \r\n to \n for Sendmail
|
||||||
;SENDMAIL_CONVERT_CRLF = true
|
;SENDMAIL_CONVERT_CRLF = true
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;[mailer.override_header]
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; This is empty by default, use it only if you know what you need it for.
|
||||||
|
;Reply-To = test@example.com, test2@example.com
|
||||||
|
;Content-Type = text/html; charset=utf-8
|
||||||
|
;In-Reply-To =
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;[email.incoming]
|
;[email.incoming]
|
||||||
|
|
|
@ -5,8 +5,6 @@ package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"html"
|
"html"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -15,6 +13,8 @@ import (
|
||||||
"code.gitea.io/gitea/modules/csv"
|
"code.gitea.io/gitea/modules/csv"
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/translation"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -81,86 +81,38 @@ func writeField(w io.Writer, element, class, field string) error {
|
||||||
func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
||||||
tmpBlock := bufio.NewWriter(output)
|
tmpBlock := bufio.NewWriter(output)
|
||||||
maxSize := setting.UI.CSV.MaxFileSize
|
maxSize := setting.UI.CSV.MaxFileSize
|
||||||
|
maxRows := setting.UI.CSV.MaxRows
|
||||||
|
|
||||||
if maxSize == 0 {
|
if maxSize != 0 {
|
||||||
return r.tableRender(ctx, input, tmpBlock)
|
input = io.LimitReader(input, maxSize+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawBytes, err := io.ReadAll(io.LimitReader(input, maxSize+1))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if int64(len(rawBytes)) <= maxSize {
|
|
||||||
return r.tableRender(ctx, bytes.NewReader(rawBytes), tmpBlock)
|
|
||||||
}
|
|
||||||
return r.fallbackRender(io.MultiReader(bytes.NewReader(rawBytes), input), tmpBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error {
|
|
||||||
_, err := tmpBlock.WriteString("<pre>")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
scan := bufio.NewScanner(input)
|
|
||||||
scan.Split(bufio.ScanRunes)
|
|
||||||
for scan.Scan() {
|
|
||||||
switch scan.Text() {
|
|
||||||
case `&`:
|
|
||||||
_, err = tmpBlock.WriteString("&")
|
|
||||||
case `'`:
|
|
||||||
_, err = tmpBlock.WriteString("'") // "'" is shorter than "'" and apos was not in HTML until HTML5.
|
|
||||||
case `<`:
|
|
||||||
_, err = tmpBlock.WriteString("<")
|
|
||||||
case `>`:
|
|
||||||
_, err = tmpBlock.WriteString(">")
|
|
||||||
case `"`:
|
|
||||||
_, err = tmpBlock.WriteString(""") // """ is shorter than """.
|
|
||||||
default:
|
|
||||||
_, err = tmpBlock.Write(scan.Bytes())
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = scan.Err(); err != nil {
|
|
||||||
return fmt.Errorf("fallbackRender scan: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tmpBlock.WriteString("</pre>")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return tmpBlock.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock *bufio.Writer) error {
|
|
||||||
rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input)
|
rd, err := csv.CreateReaderAndDetermineDelimiter(ctx, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil {
|
if _, err := tmpBlock.WriteString(`<table class="data-table">`); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
row := 1
|
|
||||||
|
row := 0
|
||||||
for {
|
for {
|
||||||
fields, err := rd.Read()
|
fields, err := rd.Read()
|
||||||
if err == io.EOF {
|
if err == io.EOF || (row >= maxRows && maxRows != 0) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := tmpBlock.WriteString("<tr>"); err != nil {
|
if _, err := tmpBlock.WriteString("<tr>"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
element := "td"
|
element := "td"
|
||||||
if row == 1 {
|
if row == 0 {
|
||||||
element = "th"
|
element = "th"
|
||||||
}
|
}
|
||||||
if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row)); err != nil {
|
if err := writeField(tmpBlock, element, "line-num", strconv.Itoa(row+1)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
|
@ -174,8 +126,32 @@ func (Renderer) tableRender(ctx *markup.RenderContext, input io.Reader, tmpBlock
|
||||||
|
|
||||||
row++
|
row++
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = tmpBlock.WriteString("</table>"); err != nil {
|
if _, err = tmpBlock.WriteString("</table>"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if maxRows or maxSize is reached, and if true, warn.
|
||||||
|
if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) {
|
||||||
|
warn := `<table class="data-table"><tr><td>`
|
||||||
|
rawLink := ` <a href="` + ctx.Links.RawLink() + `/` + util.PathEscapeSegments(ctx.RelativePath) + `">`
|
||||||
|
|
||||||
|
// Try to get the user translation
|
||||||
|
if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok {
|
||||||
|
warn += locale.TrString("repo.file_too_large")
|
||||||
|
rawLink += locale.TrString("repo.file_view_raw")
|
||||||
|
} else {
|
||||||
|
warn += "The file is too large to be shown."
|
||||||
|
rawLink += "View Raw"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn += rawLink + `</a></td></tr></table>`
|
||||||
|
|
||||||
|
// Write the HTML string to the output
|
||||||
|
if _, err := tmpBlock.WriteString(warn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return tmpBlock.Flush()
|
return tmpBlock.Flush()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
package markup
|
package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -31,12 +29,4 @@ func TestRenderCSV(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, v, buf.String())
|
assert.EqualValues(t, v, buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("fallbackRender", func(t *testing.T) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := render.fallbackRender(strings.NewReader("1,<a>\n2,<b>"), bufio.NewWriter(&buf))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
want := "<pre>1,<a>\n2,<b></pre>"
|
|
||||||
assert.Equal(t, want, buf.String())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,15 @@ import (
|
||||||
// Mailer represents mail service.
|
// Mailer represents mail service.
|
||||||
type Mailer struct {
|
type Mailer struct {
|
||||||
// Mailer
|
// Mailer
|
||||||
Name string `ini:"NAME"`
|
Name string `ini:"NAME"`
|
||||||
From string `ini:"FROM"`
|
From string `ini:"FROM"`
|
||||||
EnvelopeFrom string `ini:"ENVELOPE_FROM"`
|
EnvelopeFrom string `ini:"ENVELOPE_FROM"`
|
||||||
OverrideEnvelopeFrom bool `ini:"-"`
|
OverrideEnvelopeFrom bool `ini:"-"`
|
||||||
FromName string `ini:"-"`
|
FromName string `ini:"-"`
|
||||||
FromEmail string `ini:"-"`
|
FromEmail string `ini:"-"`
|
||||||
SendAsPlainText bool `ini:"SEND_AS_PLAIN_TEXT"`
|
SendAsPlainText bool `ini:"SEND_AS_PLAIN_TEXT"`
|
||||||
SubjectPrefix string `ini:"SUBJECT_PREFIX"`
|
SubjectPrefix string `ini:"SUBJECT_PREFIX"`
|
||||||
|
OverrideHeader map[string][]string `ini:"-"`
|
||||||
|
|
||||||
// SMTP sender
|
// SMTP sender
|
||||||
Protocol string `ini:"PROTOCOL"`
|
Protocol string `ini:"PROTOCOL"`
|
||||||
|
@ -159,6 +160,12 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||||
log.Fatal("Unable to map [mailer] section on to MailService. Error: %v", err)
|
log.Fatal("Unable to map [mailer] section on to MailService. Error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overrideHeader := rootCfg.Section("mailer.override_header").Keys()
|
||||||
|
MailService.OverrideHeader = make(map[string][]string)
|
||||||
|
for _, key := range overrideHeader {
|
||||||
|
MailService.OverrideHeader[key.Name()] = key.Strings(",")
|
||||||
|
}
|
||||||
|
|
||||||
// Infer SMTPPort if not set
|
// Infer SMTPPort if not set
|
||||||
if MailService.SMTPPort == "" {
|
if MailService.SMTPPort == "" {
|
||||||
switch MailService.Protocol {
|
switch MailService.Protocol {
|
||||||
|
|
|
@ -53,6 +53,7 @@ var UI = struct {
|
||||||
|
|
||||||
CSV struct {
|
CSV struct {
|
||||||
MaxFileSize int64
|
MaxFileSize int64
|
||||||
|
MaxRows int
|
||||||
} `ini:"ui.csv"`
|
} `ini:"ui.csv"`
|
||||||
|
|
||||||
Admin struct {
|
Admin struct {
|
||||||
|
@ -110,8 +111,10 @@ var UI = struct {
|
||||||
},
|
},
|
||||||
CSV: struct {
|
CSV: struct {
|
||||||
MaxFileSize int64
|
MaxFileSize int64
|
||||||
|
MaxRows int
|
||||||
}{
|
}{
|
||||||
MaxFileSize: 524288,
|
MaxFileSize: 524288,
|
||||||
|
MaxRows: 2500,
|
||||||
},
|
},
|
||||||
Admin: struct {
|
Admin: struct {
|
||||||
UserPagingNum int
|
UserPagingNum int
|
||||||
|
|
|
@ -6,8 +6,11 @@ package structs
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Activity struct {
|
type Activity struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
UserID int64 `json:"user_id"` // Receiver user
|
UserID int64 `json:"user_id"` // Receiver user
|
||||||
|
// the type of action
|
||||||
|
//
|
||||||
|
// 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
|
||||||
OpType string `json:"op_type"`
|
OpType string `json:"op_type"`
|
||||||
ActUserID int64 `json:"act_user_id"`
|
ActUserID int64 `json:"act_user_id"`
|
||||||
ActUser *User `json:"act_user"`
|
ActUser *User `json:"act_user"`
|
||||||
|
|
44
options/gitignore/Alteryx
Normal file
44
options/gitignore/Alteryx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# gitignore template for Alteryx Designer
|
||||||
|
# website: https://www.alteryx.com/
|
||||||
|
# website: https://help.alteryx.com/current/designer/alteryx-file-types
|
||||||
|
|
||||||
|
# Alteryx Data Files
|
||||||
|
*.yxdb
|
||||||
|
*.cydb
|
||||||
|
*.cyidx
|
||||||
|
*.rptx
|
||||||
|
*.vvf
|
||||||
|
*.aws
|
||||||
|
|
||||||
|
# Alteryx Special Files
|
||||||
|
*.yxwv
|
||||||
|
*.yxft
|
||||||
|
*.yxbe
|
||||||
|
*.bak
|
||||||
|
*.pcxml
|
||||||
|
*.log
|
||||||
|
*.bin
|
||||||
|
*.yxlang
|
||||||
|
CASS.ini
|
||||||
|
|
||||||
|
# Alteryx License Files
|
||||||
|
*.yxlc
|
||||||
|
*.slc
|
||||||
|
*.cylc
|
||||||
|
*.alc
|
||||||
|
*.gzlc
|
||||||
|
|
||||||
|
## gitignore reference sites
|
||||||
|
# https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#Ignoring-Files
|
||||||
|
# https://git-scm.com/docs/gitignore
|
||||||
|
# https://help.github.com/articles/ignoring-files/
|
||||||
|
|
||||||
|
## Useful knowledge from stackoverflow
|
||||||
|
# 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.
|
||||||
|
# WARNING: First commit your current changes, or you will lose them.
|
||||||
|
# Then run the following commands from the top folder of your git repo:
|
||||||
|
# git rm -r --cached .
|
||||||
|
# git add .
|
||||||
|
# git commit -m "fixed untracked files"
|
||||||
|
|
||||||
|
# author: Kacper Ksieski
|
|
@ -14,6 +14,8 @@
|
||||||
*.lzma
|
*.lzma
|
||||||
*.cab
|
*.cab
|
||||||
*.xar
|
*.xar
|
||||||
|
*.zst
|
||||||
|
*.tzst
|
||||||
|
|
||||||
# Packing-only formats
|
# Packing-only formats
|
||||||
*.iso
|
*.iso
|
||||||
|
|
11
options/gitignore/Ballerina
Normal file
11
options/gitignore/Ballerina
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# generated files
|
||||||
|
target/
|
||||||
|
generated/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
Dependencies.toml
|
||||||
|
|
||||||
|
# config files
|
||||||
|
Config.toml
|
||||||
|
# the config files used for testing, Uncomment the following line if you want to commit the test config files
|
||||||
|
#!**/tests/Config.toml
|
|
@ -9,3 +9,4 @@ install_manifest.txt
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
_deps
|
_deps
|
||||||
|
CMakeUserPresets.json
|
||||||
|
|
|
@ -26,6 +26,18 @@
|
||||||
#*.obj
|
#*.obj
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Default Delphi compiler directories
|
||||||
|
# Content of this directories are generated with each Compile/Construct of a project.
|
||||||
|
# Most of the time, files here have not there place in a code repository.
|
||||||
|
#Win32/
|
||||||
|
#Win64/
|
||||||
|
#OSX64/
|
||||||
|
#OSXARM64/
|
||||||
|
#Android/
|
||||||
|
#Android64/
|
||||||
|
#iOSDevice64/
|
||||||
|
#Linux64/
|
||||||
|
|
||||||
# Delphi compiler-generated binaries (safe to delete)
|
# Delphi compiler-generated binaries (safe to delete)
|
||||||
*.exe
|
*.exe
|
||||||
*.dll
|
*.dll
|
||||||
|
|
18
options/gitignore/GitHubPages
Normal file
18
options/gitignore/GitHubPages
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# This .gitignore is appropriate for repositories deployed to GitHub Pages and using
|
||||||
|
# a Gemfile as specified at https://github.com/github/pages-gem#conventional
|
||||||
|
|
||||||
|
# Basic Jekyll gitignores (synchronize to Jekyll.gitignore)
|
||||||
|
_site/
|
||||||
|
.sass-cache/
|
||||||
|
.jekyll-cache/
|
||||||
|
.jekyll-metadata
|
||||||
|
|
||||||
|
# Additional Ruby/bundler ignore for when you run: bundle install
|
||||||
|
/vendor
|
||||||
|
|
||||||
|
# Specific ignore for GitHub Pages
|
||||||
|
# GitHub Pages will always use its own deployed version of pages-gem
|
||||||
|
# This means GitHub Pages will NOT use your Gemfile.lock and therefore it is
|
||||||
|
# counterproductive to check this file into the repository.
|
||||||
|
# Details at https://github.com/github/pages-gem/issues/768
|
||||||
|
Gemfile.lock
|
|
@ -20,3 +20,6 @@
|
||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
go.work
|
||||||
go.work.sum
|
go.work.sum
|
||||||
|
|
||||||
|
# env file
|
||||||
|
.env
|
||||||
|
|
|
@ -5,23 +5,6 @@
|
||||||
## User settings
|
## User settings
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
|
|
||||||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
|
||||||
*.xcscmblueprint
|
|
||||||
*.xccheckout
|
|
||||||
|
|
||||||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
|
||||||
build/
|
|
||||||
DerivedData/
|
|
||||||
*.moved-aside
|
|
||||||
*.pbxuser
|
|
||||||
!default.pbxuser
|
|
||||||
*.mode1v3
|
|
||||||
!default.mode1v3
|
|
||||||
*.mode2v3
|
|
||||||
!default.mode2v3
|
|
||||||
*.perspectivev3
|
|
||||||
!default.perspectivev3
|
|
||||||
|
|
||||||
## Obj-C/Swift specific
|
## Obj-C/Swift specific
|
||||||
*.hmap
|
*.hmap
|
||||||
|
|
||||||
|
|
|
@ -12,3 +12,10 @@ Cargo.lock
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
*.pdb
|
*.pdb
|
||||||
|
|
||||||
|
# RustRover
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
|
@ -5,23 +5,6 @@
|
||||||
## User settings
|
## User settings
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
|
|
||||||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
|
||||||
*.xcscmblueprint
|
|
||||||
*.xccheckout
|
|
||||||
|
|
||||||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
|
||||||
build/
|
|
||||||
DerivedData/
|
|
||||||
*.moved-aside
|
|
||||||
*.pbxuser
|
|
||||||
!default.pbxuser
|
|
||||||
*.mode1v3
|
|
||||||
!default.mode1v3
|
|
||||||
*.mode2v3
|
|
||||||
!default.mode2v3
|
|
||||||
*.perspectivev3
|
|
||||||
!default.perspectivev3
|
|
||||||
|
|
||||||
## Obj-C/Swift specific
|
## Obj-C/Swift specific
|
||||||
*.hmap
|
*.hmap
|
||||||
|
|
||||||
|
@ -66,10 +49,6 @@ playground.xcworkspace
|
||||||
|
|
||||||
Carthage/Build/
|
Carthage/Build/
|
||||||
|
|
||||||
# Accio dependency management
|
|
||||||
Dependencies/
|
|
||||||
.accio/
|
|
||||||
|
|
||||||
# fastlane
|
# fastlane
|
||||||
#
|
#
|
||||||
# It is recommended to not store the screenshots in the git repo.
|
# It is recommended to not store the screenshots in the git repo.
|
||||||
|
@ -81,10 +60,3 @@ fastlane/report.xml
|
||||||
fastlane/Preview.html
|
fastlane/Preview.html
|
||||||
fastlane/screenshots/**/*.png
|
fastlane/screenshots/**/*.png
|
||||||
fastlane/test_output
|
fastlane/test_output
|
||||||
|
|
||||||
# Code Injection
|
|
||||||
#
|
|
||||||
# After new code Injection tools there's a generated folder /iOSInjectionProject
|
|
||||||
# https://github.com/johnno1962/injectionforxcode
|
|
||||||
|
|
||||||
iOSInjectionProject/
|
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
*.synctex.gz
|
*.synctex.gz
|
||||||
*.synctex.gz(busy)
|
*.synctex.gz(busy)
|
||||||
*.pdfsync
|
*.pdfsync
|
||||||
|
*.rubbercache
|
||||||
|
rubber.cache
|
||||||
|
|
||||||
## Build tool directories for auxiliary files
|
## Build tool directories for auxiliary files
|
||||||
# latexrun
|
# latexrun
|
||||||
|
@ -138,6 +140,9 @@ acs-*.bib
|
||||||
*.trc
|
*.trc
|
||||||
*.xref
|
*.xref
|
||||||
|
|
||||||
|
# hypdoc
|
||||||
|
*.hd
|
||||||
|
|
||||||
# hyperref
|
# hyperref
|
||||||
*.brf
|
*.brf
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,9 @@ override.tf.json
|
||||||
*_override.tf
|
*_override.tf
|
||||||
*_override.tf.json
|
*_override.tf.json
|
||||||
|
|
||||||
|
# Ignore transient lock info files created by terraform apply
|
||||||
|
.terraform.tfstate.lock.info
|
||||||
|
|
||||||
# Include override files you do wish to add to version control using negated pattern
|
# Include override files you do wish to add to version control using negated pattern
|
||||||
# !example_override.tf
|
# !example_override.tf
|
||||||
|
|
||||||
|
@ -32,3 +35,6 @@ override.tf.json
|
||||||
# Ignore CLI configuration files
|
# Ignore CLI configuration files
|
||||||
.terraformrc
|
.terraformrc
|
||||||
terraform.rc
|
terraform.rc
|
||||||
|
|
||||||
|
# Ignore hcl file
|
||||||
|
.terraform.lock.hcl
|
||||||
|
|
11
options/gitignore/UiPath
Normal file
11
options/gitignore/UiPath
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# gitignore template for RPA development using UiPath Studio
|
||||||
|
# website: https://www.uipath.com/product/studio
|
||||||
|
#
|
||||||
|
# Recommended: n/a
|
||||||
|
|
||||||
|
# Ignore folders that could cause issues if accidentally tracked
|
||||||
|
**/.local/**
|
||||||
|
**/.settings/**
|
||||||
|
**/.objects/**
|
||||||
|
**/.tmh/**
|
||||||
|
**/*.log
|
|
@ -47,7 +47,7 @@ SourceArt/**/*.tga
|
||||||
|
|
||||||
# Binary Files
|
# Binary Files
|
||||||
Binaries/*
|
Binaries/*
|
||||||
Plugins/*/Binaries/*
|
Plugins/**/Binaries/*
|
||||||
|
|
||||||
# Builds
|
# Builds
|
||||||
Build/*
|
Build/*
|
||||||
|
@ -68,7 +68,7 @@ Saved/*
|
||||||
|
|
||||||
# Compiled source files for the engine to use
|
# Compiled source files for the engine to use
|
||||||
Intermediate/*
|
Intermediate/*
|
||||||
Plugins/*/Intermediate/*
|
Plugins/**/Intermediate/*
|
||||||
|
|
||||||
# Cache files for the editor to use
|
# Cache files for the editor to use
|
||||||
DerivedDataCache/*
|
DerivedDataCache/*
|
||||||
|
|
|
@ -1,6 +1,2 @@
|
||||||
## User settings
|
## User settings
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
|
|
||||||
## Xcode 8 and earlier
|
|
||||||
*.xcscmblueprint
|
|
||||||
*.xccheckout
|
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "forgejo"
|
package-mode = false
|
||||||
version = "0.0.0"
|
|
||||||
description = ""
|
|
||||||
authors = []
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.10"
|
python = "^3.10"
|
||||||
|
|
3
release-notes/8.0.0/feat/4083.md
Normal file
3
release-notes/8.0.0/feat/4083.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
- add [Reviewed-on and Reviewed-by variables](https://codeberg.org/forgejo/forgejo/commit/4ddd9af50fbfcfb2ebf629697a803b3bce56c4af) to the merge template
|
||||||
|
- [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)
|
||||||
|
- [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
Normal file
1
release-notes/8.0.0/fix/4083.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
- [NuGet Package fails `choco info pkgname` when `pkgname` is also a substring of another package Id](https://codeberg.org/forgejo/forgejo/commit/c6e04c3c9eddfa6c4bec541f681c8d300b157cdb)
|
|
@ -96,20 +96,34 @@ func FeedCapabilityResource(ctx *context.Context) {
|
||||||
xmlResponse(ctx, http.StatusOK, Metadata)
|
xmlResponse(ctx, http.StatusOK, Metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
var searchTermExtract = regexp.MustCompile(`'([^']+)'`)
|
var (
|
||||||
|
searchTermExtract = regexp.MustCompile(`'([^']+)'`)
|
||||||
|
searchTermExact = regexp.MustCompile(`\s+eq\s+'`)
|
||||||
|
)
|
||||||
|
|
||||||
func getSearchTerm(ctx *context.Context) string {
|
func getSearchTerm(ctx *context.Context) packages_model.SearchValue {
|
||||||
searchTerm := strings.Trim(ctx.FormTrim("searchTerm"), "'")
|
searchTerm := strings.Trim(ctx.FormTrim("searchTerm"), "'")
|
||||||
if searchTerm == "" {
|
if searchTerm != "" {
|
||||||
// $filter contains a query like:
|
return packages_model.SearchValue{
|
||||||
// (((Id ne null) and substringof('microsoft',tolower(Id)))
|
Value: searchTerm,
|
||||||
// We don't support these queries, just extract the search term.
|
ExactMatch: false,
|
||||||
match := searchTermExtract.FindStringSubmatch(ctx.FormTrim("$filter"))
|
|
||||||
if len(match) == 2 {
|
|
||||||
searchTerm = strings.TrimSpace(match[1])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return searchTerm
|
|
||||||
|
// $filter contains a query like:
|
||||||
|
// (((Id ne null) and substringof('microsoft',tolower(Id)))
|
||||||
|
// https://www.odata.org/documentation/odata-version-2-0/uri-conventions/ section 4.5
|
||||||
|
// We don't support these queries, just extract the search term.
|
||||||
|
filter := ctx.FormTrim("$filter")
|
||||||
|
match := searchTermExtract.FindStringSubmatch(filter)
|
||||||
|
if len(match) == 2 {
|
||||||
|
return packages_model.SearchValue{
|
||||||
|
Value: strings.TrimSpace(match[1]),
|
||||||
|
ExactMatch: searchTermExact.MatchString(filter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packages_model.SearchValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs
|
// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs
|
||||||
|
@ -118,11 +132,9 @@ func SearchServiceV2(ctx *context.Context) {
|
||||||
paginator := db.NewAbsoluteListOptions(skip, take)
|
paginator := db.NewAbsoluteListOptions(skip, take)
|
||||||
|
|
||||||
pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
|
pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
|
||||||
OwnerID: ctx.Package.Owner.ID,
|
OwnerID: ctx.Package.Owner.ID,
|
||||||
Type: packages_model.TypeNuGet,
|
Type: packages_model.TypeNuGet,
|
||||||
Name: packages_model.SearchValue{
|
Name: getSearchTerm(ctx),
|
||||||
Value: getSearchTerm(ctx),
|
|
||||||
},
|
|
||||||
IsInternal: optional.Some(false),
|
IsInternal: optional.Some(false),
|
||||||
Paginator: paginator,
|
Paginator: paginator,
|
||||||
})
|
})
|
||||||
|
@ -169,10 +181,8 @@ func SearchServiceV2(ctx *context.Context) {
|
||||||
// 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
|
// 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
|
||||||
func SearchServiceV2Count(ctx *context.Context) {
|
func SearchServiceV2Count(ctx *context.Context) {
|
||||||
count, err := nuget_model.CountPackages(ctx, &packages_model.PackageSearchOptions{
|
count, err := nuget_model.CountPackages(ctx, &packages_model.PackageSearchOptions{
|
||||||
OwnerID: ctx.Package.Owner.ID,
|
OwnerID: ctx.Package.Owner.ID,
|
||||||
Name: packages_model.SearchValue{
|
Name: getSearchTerm(ctx),
|
||||||
Value: getSearchTerm(ctx),
|
|
||||||
},
|
|
||||||
IsInternal: optional.Some(false),
|
IsInternal: optional.Some(false),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -158,7 +158,7 @@ func GetContentHistoryDetail(ctx *context.Context) {
|
||||||
|
|
||||||
// use chroma to render the diff html
|
// use chroma to render the diff html
|
||||||
diffHTMLBuf := bytes.Buffer{}
|
diffHTMLBuf := bytes.Buffer{}
|
||||||
diffHTMLBuf.WriteString("<pre class='chroma' style='tab-size: 4'>")
|
diffHTMLBuf.WriteString("<pre class='chroma'>")
|
||||||
for _, it := range diff {
|
for _, it := range diff {
|
||||||
if it.Type == diffmatchpatch.DiffInsert {
|
if it.Type == diffmatchpatch.DiffInsert {
|
||||||
diffHTMLBuf.WriteString("<span class='gi'>")
|
diffHTMLBuf.WriteString("<span class='gi'>")
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (m *Message) ToMessage() *gomail.Message {
|
||||||
msg.SetHeader(header, m.Headers[header]...)
|
msg.SetHeader(header, m.Headers[header]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(setting.MailService.SubjectPrefix) > 0 {
|
if setting.MailService.SubjectPrefix != "" {
|
||||||
msg.SetHeader("Subject", setting.MailService.SubjectPrefix+" "+m.Subject)
|
msg.SetHeader("Subject", setting.MailService.SubjectPrefix+" "+m.Subject)
|
||||||
} else {
|
} else {
|
||||||
msg.SetHeader("Subject", m.Subject)
|
msg.SetHeader("Subject", m.Subject)
|
||||||
|
@ -79,6 +79,14 @@ func (m *Message) ToMessage() *gomail.Message {
|
||||||
if len(msg.GetHeader("Message-ID")) == 0 {
|
if len(msg.GetHeader("Message-ID")) == 0 {
|
||||||
msg.SetHeader("Message-ID", m.generateAutoMessageID())
|
msg.SetHeader("Message-ID", m.generateAutoMessageID())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for k, v := range setting.MailService.OverrideHeader {
|
||||||
|
if len(msg.GetHeader(k)) != 0 {
|
||||||
|
log.Debug("Mailer override header '%s' as per config", k)
|
||||||
|
}
|
||||||
|
msg.SetHeader(k, v...)
|
||||||
|
}
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,22 +4,22 @@
|
||||||
package mailer
|
package mailer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateMessageID(t *testing.T) {
|
func TestGenerateMessageID(t *testing.T) {
|
||||||
mailService := setting.Mailer{
|
defer test.MockVariableValue(&setting.MailService, &setting.Mailer{
|
||||||
From: "test@gitea.com",
|
From: "test@gitea.com",
|
||||||
}
|
})()
|
||||||
|
defer test.MockVariableValue(&setting.Domain, "localhost")()
|
||||||
setting.MailService = &mailService
|
|
||||||
setting.Domain = "localhost"
|
|
||||||
|
|
||||||
date := time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC)
|
date := time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC)
|
||||||
m := NewMessageFrom("", "display-name", "from-address", "subject", "body")
|
m := NewMessageFrom("", "display-name", "from-address", "subject", "body")
|
||||||
|
@ -39,7 +39,7 @@ func TestGenerateMessageID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateMessageIDForRelease(t *testing.T) {
|
func TestGenerateMessageIDForRelease(t *testing.T) {
|
||||||
setting.Domain = "localhost"
|
defer test.MockVariableValue(&setting.Domain, "localhost")()
|
||||||
|
|
||||||
rel := repo_model.Release{
|
rel := repo_model.Release{
|
||||||
ID: 42,
|
ID: 42,
|
||||||
|
@ -51,3 +51,77 @@ func TestGenerateMessageIDForRelease(t *testing.T) {
|
||||||
m := createMessageIDForRelease(&rel)
|
m := createMessageIDForRelease(&rel)
|
||||||
assert.Equal(t, "<test/tag-test/releases/42@localhost>", m)
|
assert.Equal(t, "<test/tag-test/releases/42@localhost>", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestToMessage(t *testing.T) {
|
||||||
|
defer test.MockVariableValue(&setting.MailService, &setting.Mailer{
|
||||||
|
From: "test@gitea.com",
|
||||||
|
})()
|
||||||
|
defer test.MockVariableValue(&setting.Domain, "localhost")()
|
||||||
|
|
||||||
|
m1 := Message{
|
||||||
|
Info: "info",
|
||||||
|
FromAddress: "test@gitea.com",
|
||||||
|
FromDisplayName: "Test Gitea",
|
||||||
|
To: "a@b.com",
|
||||||
|
Subject: "Issue X Closed",
|
||||||
|
Body: "Some Issue got closed by Y-Man",
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &strings.Builder{}
|
||||||
|
_, err := m1.ToMessage().WriteTo(buf)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
header, _ := extractMailHeaderAndContent(t, buf.String())
|
||||||
|
assert.EqualValues(t, map[string]string{
|
||||||
|
"Content-Type": "multipart/alternative;",
|
||||||
|
"Date": "Mon, 01 Jan 0001 00:00:00 +0000",
|
||||||
|
"From": "\"Test Gitea\" <test@gitea.com>",
|
||||||
|
"Message-ID": "<autogen--6795364578871-69c000786adc60dc@localhost>",
|
||||||
|
"Mime-Version": "1.0",
|
||||||
|
"Subject": "Issue X Closed",
|
||||||
|
"To": "a@b.com",
|
||||||
|
"X-Auto-Response-Suppress": "All",
|
||||||
|
}, header)
|
||||||
|
|
||||||
|
setting.MailService.OverrideHeader = map[string][]string{
|
||||||
|
"Message-ID": {""}, // delete message id
|
||||||
|
"Auto-Submitted": {"auto-generated"}, // suppress auto replay
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = &strings.Builder{}
|
||||||
|
_, err = m1.ToMessage().WriteTo(buf)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
header, _ = extractMailHeaderAndContent(t, buf.String())
|
||||||
|
assert.EqualValues(t, map[string]string{
|
||||||
|
"Content-Type": "multipart/alternative;",
|
||||||
|
"Date": "Mon, 01 Jan 0001 00:00:00 +0000",
|
||||||
|
"From": "\"Test Gitea\" <test@gitea.com>",
|
||||||
|
"Message-ID": "",
|
||||||
|
"Mime-Version": "1.0",
|
||||||
|
"Subject": "Issue X Closed",
|
||||||
|
"To": "a@b.com",
|
||||||
|
"X-Auto-Response-Suppress": "All",
|
||||||
|
"Auto-Submitted": "auto-generated",
|
||||||
|
}, header)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractMailHeaderAndContent(t *testing.T, mail string) (map[string]string, string) {
|
||||||
|
header := make(map[string]string)
|
||||||
|
|
||||||
|
parts := strings.SplitN(mail, "boundary=", 2)
|
||||||
|
if !assert.Len(t, parts, 2) {
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
content := strings.TrimSpace("boundary=" + parts[1])
|
||||||
|
|
||||||
|
hParts := strings.Split(parts[0], "\n")
|
||||||
|
|
||||||
|
for _, hPart := range hParts {
|
||||||
|
parts := strings.SplitN(hPart, ":", 2)
|
||||||
|
hk := strings.TrimSpace(parts[0])
|
||||||
|
if hk != "" {
|
||||||
|
header[hk] = strings.TrimSpace(parts[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return header, content
|
||||||
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ func newDownloader(ctx context.Context, ownerName string, opts base.MigrateOptio
|
||||||
// migrateRepository will download information and then upload it to Uploader, this is a simple
|
// migrateRepository will download information and then upload it to Uploader, this is a simple
|
||||||
// process for small repository. For a big repository, save all the data to disk
|
// process for small repository. For a big repository, save all the data to disk
|
||||||
// before upload is better
|
// before upload is better
|
||||||
func migrateRepository(ctx context.Context, doer *user_model.User, downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error {
|
func migrateRepository(_ context.Context, doer *user_model.User, downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error {
|
||||||
if messenger == nil {
|
if messenger == nil {
|
||||||
messenger = base.NilMessenger
|
messenger = base.NilMessenger
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,9 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
|
||||||
if err := pr.Issue.LoadPoster(ctx); err != nil {
|
if err := pr.Issue.LoadPoster(ctx); err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
if err := pr.Issue.LoadRepo(ctx); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
isExternalTracker := pr.BaseRepo.UnitEnabled(ctx, unit.TypeExternalTracker)
|
isExternalTracker := pr.BaseRepo.UnitEnabled(ctx, unit.TypeExternalTracker)
|
||||||
issueReference := "#"
|
issueReference := "#"
|
||||||
|
@ -53,6 +56,9 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
|
||||||
issueReference = "!"
|
issueReference = "!"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reviewedOn := fmt.Sprintf("Reviewed-on: %s/%s", setting.AppURL, pr.Issue.Link())
|
||||||
|
reviewedBy := pr.GetApprovers(ctx)
|
||||||
|
|
||||||
if mergeStyle != "" {
|
if mergeStyle != "" {
|
||||||
commit, err := baseGitRepo.GetBranchCommit(pr.BaseRepo.DefaultBranch)
|
commit, err := baseGitRepo.GetBranchCommit(pr.BaseRepo.DefaultBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -83,6 +89,8 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
|
||||||
"PullRequestPosterName": pr.Issue.Poster.Name,
|
"PullRequestPosterName": pr.Issue.Poster.Name,
|
||||||
"PullRequestIndex": strconv.FormatInt(pr.Index, 10),
|
"PullRequestIndex": strconv.FormatInt(pr.Index, 10),
|
||||||
"PullRequestReference": fmt.Sprintf("%s%d", issueReference, pr.Index),
|
"PullRequestReference": fmt.Sprintf("%s%d", issueReference, pr.Index),
|
||||||
|
"ReviewedOn": reviewedOn,
|
||||||
|
"ReviewedBy": reviewedBy,
|
||||||
}
|
}
|
||||||
if pr.HeadRepo != nil {
|
if pr.HeadRepo != nil {
|
||||||
vars["HeadRepoOwnerName"] = pr.HeadRepo.OwnerName
|
vars["HeadRepoOwnerName"] = pr.HeadRepo.OwnerName
|
||||||
|
@ -122,20 +130,22 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
|
||||||
return "", "", nil
|
return "", "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body = fmt.Sprintf("%s\n%s", reviewedOn, reviewedBy)
|
||||||
|
|
||||||
// Squash merge has a different from other styles.
|
// Squash merge has a different from other styles.
|
||||||
if mergeStyle == repo_model.MergeStyleSquash {
|
if mergeStyle == repo_model.MergeStyleSquash {
|
||||||
return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), "", nil
|
return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if pr.BaseRepoID == pr.HeadRepoID {
|
if pr.BaseRepoID == pr.HeadRepoID {
|
||||||
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
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
if pr.HeadRepo == nil {
|
if pr.HeadRepo == nil {
|
||||||
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
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func expandDefaultMergeMessage(template string, vars map[string]string) (message, body string) {
|
func expandDefaultMergeMessage(template string, vars map[string]string) (message, body string) {
|
||||||
|
|
|
@ -285,7 +285,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
|
||||||
}
|
}
|
||||||
|
|
||||||
// changeRepositoryName changes all corresponding setting from old repository name to new one.
|
// changeRepositoryName changes all corresponding setting from old repository name to new one.
|
||||||
func changeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, newRepoName string) (err error) {
|
func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newRepoName string) (err error) {
|
||||||
oldRepoName := repo.Name
|
oldRepoName := repo.Name
|
||||||
newRepoName = strings.ToLower(newRepoName)
|
newRepoName = strings.ToLower(newRepoName)
|
||||||
if err = repo_model.IsUsableRepoName(newRepoName); err != nil {
|
if err = repo_model.IsUsableRepoName(newRepoName); err != nil {
|
||||||
|
@ -347,7 +347,7 @@ func ChangeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo
|
||||||
// local copy's origin accordingly.
|
// local copy's origin accordingly.
|
||||||
|
|
||||||
repoWorkingPool.CheckIn(fmt.Sprint(repo.ID))
|
repoWorkingPool.CheckIn(fmt.Sprint(repo.ID))
|
||||||
if err := changeRepositoryName(ctx, doer, repo, newRepoName); err != nil {
|
if err := changeRepositoryName(ctx, repo, newRepoName); err != nil {
|
||||||
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
|
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {readFileSync} from 'node:fs';
|
import {readFileSync} from 'node:fs';
|
||||||
import {env} from 'node:process';
|
import {env} from 'node:process';
|
||||||
import {parse} from 'postcss';
|
import {parse} from 'postcss';
|
||||||
|
import plugin from 'tailwindcss/plugin.js';
|
||||||
|
|
||||||
const isProduction = env.NODE_ENV !== 'development';
|
const isProduction = env.NODE_ENV !== 'development';
|
||||||
|
|
||||||
|
@ -98,4 +99,26 @@ export default {
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
plugin(({addUtilities}) => {
|
||||||
|
addUtilities({
|
||||||
|
// tw-hidden must win all other "display: xxx !important" classes to get the chance to "hide" an element.
|
||||||
|
// do not use:
|
||||||
|
// * "[hidden]" attribute: it's too weak, can not be applied to an element with "display: flex"
|
||||||
|
// * ".hidden" class: it has been polluted by Fomantic UI in many cases
|
||||||
|
// * inline style="display: none": it's difficult to tweak
|
||||||
|
// * jQuery's show/hide/toggle: it can not show/hide elements with "display: xxx !important"
|
||||||
|
// only use:
|
||||||
|
// * this ".tw-hidden" class
|
||||||
|
// * showElem/hideElem/toggleElem functions in "utils/dom.js"
|
||||||
|
'.hidden.hidden': {
|
||||||
|
'display': 'none',
|
||||||
|
},
|
||||||
|
// proposed class from https://github.com/tailwindlabs/tailwindcss/pull/12128
|
||||||
|
'.break-anywhere': {
|
||||||
|
'overflow-wrap': 'anywhere',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{{ctx.Locale.Tr "admin.packages.total_size" (ctx.Locale.TrSize .TotalBlobSize)}},
|
{{ctx.Locale.Tr "admin.packages.total_size" (ctx.Locale.TrSize .TotalBlobSize)}},
|
||||||
{{ctx.Locale.Tr "admin.packages.unreferenced_size" (ctx.Locale.TrSize .TotalUnreferencedBlobSize)}})
|
{{ctx.Locale.Tr "admin.packages.unreferenced_size" (ctx.Locale.TrSize .TotalUnreferencedBlobSize)}})
|
||||||
<div class="ui right">
|
<div class="ui right">
|
||||||
<form method="post" action="/admin/packages/cleanup">
|
<form method="post" action="{{AppSubUrl}}/admin/packages/cleanup">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
<button class="ui primary tiny button">{{ctx.Locale.Tr "admin.packages.cleanup"}}</button>
|
<button class="ui primary tiny button">{{ctx.Locale.Tr "admin.packages.cleanup"}}</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<div><label><input name="check" type="checkbox"> check</label></div>
|
<div><label><input name="check" type="checkbox"> check</label></div>
|
||||||
<div><button name="btn">submit post</button></div>
|
<div><button name="btn">submit post</button></div>
|
||||||
</form>
|
</form>
|
||||||
<form method="post" action="/no-such-uri" class="form-fetch-action">
|
<form method="post" action="no-such-uri" class="form-fetch-action">
|
||||||
<div class="tw-py-8">bad action url</div>
|
<div class="tw-py-8">bad action url</div>
|
||||||
<div><button name="btn">submit test</button></div>
|
<div><button name="btn">submit test</button></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
<div id="project-board">
|
<div id="project-board">
|
||||||
<div class="board {{if .CanWriteProjects}}sortable{{end}}"{{if .CanWriteProjects}} data-url="{{$.Link}}/move"{{end}}>
|
<div class="board {{if .CanWriteProjects}}sortable{{end}}"{{if .CanWriteProjects}} data-url="{{$.Link}}/move"{{end}}>
|
||||||
{{range .Columns}}
|
{{range .Columns}}
|
||||||
<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}}">
|
<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}}">
|
||||||
<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
|
<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
|
||||||
<div class="ui large label project-column-title tw-py-1">
|
<div class="ui large label project-column-title tw-py-1">
|
||||||
<div class="ui small circular grey label project-column-issue-count">
|
<div class="ui small circular grey label project-column-issue-count">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{range .RecentlyPushedNewBranches}}
|
{{range .RecentlyPushedNewBranches}}
|
||||||
<div class="ui positive message tw-flex tw-items-center">
|
<div class="ui positive message tw-flex tw-items-center tw-gap-2">
|
||||||
<div class="tw-flex-1">
|
<div class="tw-flex-1 tw-break-anywhere">
|
||||||
{{$timeSince := TimeSince .CommitTime.AsTime ctx.Locale}}
|
{{$timeSince := TimeSince .CommitTime.AsTime ctx.Locale}}
|
||||||
{{$repo := .GetRepo $.Context}}
|
{{$repo := .GetRepo $.Context}}
|
||||||
{{$name := .Name}}
|
{{$name := .Name}}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<div class="issue-card-icon">
|
<div class="issue-card-icon">
|
||||||
{{template "shared/issueicon" .}}
|
{{template "shared/issueicon" .}}
|
||||||
</div>
|
</div>
|
||||||
<a class="issue-card-title muted issue-title" href="{{.Link}}">{{.Title | RenderEmoji ctx | RenderCodeBlock}}</a>
|
<a class="issue-card-title muted issue-title tw-break-anywhere" href="{{.Link}}">{{.Title | RenderEmoji ctx | RenderCodeBlock}}</a>
|
||||||
{{if and $.isPinnedIssueCard $.Page.IsRepoAdmin}}
|
{{if and $.isPinnedIssueCard $.Page.IsRepoAdmin}}
|
||||||
<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}}">
|
<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}}">
|
||||||
{{svg "octicon-x" 16}}
|
{{svg "octicon-x" 16}}
|
||||||
|
|
|
@ -199,7 +199,6 @@
|
||||||
|
|
||||||
{{if .AllowMerge}} {{/* user is allowed to merge */}}
|
{{if .AllowMerge}} {{/* user is allowed to merge */}}
|
||||||
{{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}}
|
{{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}}
|
||||||
{{$approvers := (.Issue.PullRequest.GetApprovers ctx)}}
|
|
||||||
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}}
|
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}}
|
||||||
{{$hasPendingPullRequestMergeTip := ""}}
|
{{$hasPendingPullRequestMergeTip := ""}}
|
||||||
{{if .HasPendingPullRequestMerge}}
|
{{if .HasPendingPullRequestMerge}}
|
||||||
|
@ -208,11 +207,10 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
const issueUrl = window.location.origin + {{$.Issue.Link}};
|
|
||||||
const defaultMergeTitle = {{.DefaultMergeMessage}};
|
const defaultMergeTitle = {{.DefaultMergeMessage}};
|
||||||
const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}};
|
const defaultSquashMergeTitle = {{.DefaultSquashMergeMessage}};
|
||||||
const defaultMergeMessage = {{if .DefaultMergeBody}}{{.DefaultMergeBody}}{{else}}`Reviewed-on: ${issueUrl}\n` + {{$approvers}}{{end}};
|
const defaultMergeMessage = {{.DefaultMergeBody}};
|
||||||
const defaultSquashMergeMessage = {{if .DefaultSquashMergeBody}}{{.DefaultSquashMergeBody}}{{else}}`Reviewed-on: ${issueUrl}\n` + {{$approvers}}{{end}};
|
const defaultSquashMergeMessage = {{.DefaultSquashMergeBody}};
|
||||||
const mergeForm = {
|
const mergeForm = {
|
||||||
'baseLink': {{.Link}},
|
'baseLink': {{.Link}},
|
||||||
'textCancel': {{ctx.Locale.Tr "cancel"}},
|
'textCancel': {{ctx.Locale.Tr "cancel"}},
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
<td><p data-tooltip-content="{{.Description}}">{{.Name}}</p></td>
|
<td><p data-tooltip-content="{{.Description}}">{{.Name}}</p></td>
|
||||||
<td>{{if .Version}}{{.Version}}{{else}}{{ctx.Locale.Tr "unknown"}}{{end}}</td>
|
<td>{{if .Version}}{{.Version}}{{else}}{{ctx.Locale.Tr "unknown"}}{{end}}</td>
|
||||||
<td><span data-tooltip-content="{{.BelongsToOwnerName}}">{{.BelongsToOwnerType.LocaleString ctx.Locale}}</span></td>
|
<td><span data-tooltip-content="{{.BelongsToOwnerName}}">{{.BelongsToOwnerType.LocaleString ctx.Locale}}</span></td>
|
||||||
<td class="runner-tags">
|
<td class="tw-flex tw-flex-wrap tw-gap-2 runner-tags">
|
||||||
{{range .AgentLabels}}<span class="ui label">{{.}}</span>{{end}}
|
{{range .AgentLabels}}<span class="ui label">{{.}}</span>{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td>{{if .LastOnline}}{{TimeSinceUnix .LastOnline ctx.Locale}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</td>
|
<td>{{if .LastOnline}}{{TimeSinceUnix .LastOnline ctx.Locale}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</td>
|
||||||
|
|
30
templates/swagger/v1_json.tmpl
generated
30
templates/swagger/v1_json.tmpl
generated
|
@ -18537,7 +18537,37 @@
|
||||||
"x-go-name": "IsPrivate"
|
"x-go-name": "IsPrivate"
|
||||||
},
|
},
|
||||||
"op_type": {
|
"op_type": {
|
||||||
|
"description": "the type of action",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
"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"
|
||||||
|
],
|
||||||
"x-go-name": "OpType"
|
"x-go-name": "OpType"
|
||||||
},
|
},
|
||||||
"ref_name": {
|
"ref_name": {
|
||||||
|
|
|
@ -49,14 +49,14 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<a class="notifications-link tw-flex tw-flex-1 tw-flex-col silenced" href="{{.Link ctx}}">
|
<a class="notifications-link tw-flex tw-flex-1 tw-flex-col silenced" href="{{.Link ctx}}">
|
||||||
<div class="notifications-top-row tw-text-13">
|
<div class="notifications-top-row tw-text-13 tw-break-anywhere">
|
||||||
{{.Repository.FullName}} {{if .Issue}}<span class="text light-3">#{{.Issue.Index}}</span>{{end}}
|
{{.Repository.FullName}} {{if .Issue}}<span class="text light-3">#{{.Issue.Index}}</span>{{end}}
|
||||||
{{if eq .Status 3}}
|
{{if eq .Status 3}}
|
||||||
{{svg "octicon-pin" 13 "text blue tw-mt-0.5 tw-ml-1"}}
|
{{svg "octicon-pin" 13 "text blue tw-mt-0.5 tw-ml-1"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="notifications-bottom-row tw-text-16 tw-py-0.5">
|
<div class="notifications-bottom-row tw-text-16 tw-py-0.5">
|
||||||
<span class="issue-title">
|
<span class="issue-title tw-break-anywhere">
|
||||||
{{if .Issue}}
|
{{if .Issue}}
|
||||||
{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}
|
{{.Issue.Title | RenderEmoji $.Context | RenderCodeBlock}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
{{ctx.Locale.Tr "settings.select_permissions"}}
|
{{ctx.Locale.Tr "settings.select_permissions"}}
|
||||||
</summary>
|
</summary>
|
||||||
<p class="activity meta">
|
<p class="activity meta">
|
||||||
<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>
|
<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>
|
||||||
</p>
|
</p>
|
||||||
<div class="scoped-access-token-mount">
|
<div class="scoped-access-token-mount">
|
||||||
<scoped-access-token-selector
|
<scoped-access-token-selector
|
||||||
|
|
|
@ -431,22 +431,33 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
|
||||||
|
|
||||||
t.Run("SearchService", func(t *testing.T) {
|
t.Run("SearchService", func(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Query string
|
Query string
|
||||||
Skip int
|
Skip int
|
||||||
Take int
|
Take int
|
||||||
ExpectedTotal int64
|
ExpectedTotal int64
|
||||||
ExpectedResults int
|
ExpectedResults int
|
||||||
|
ExpectedExactMatch bool
|
||||||
}{
|
}{
|
||||||
{"", 0, 0, 1, 1},
|
{"", 0, 0, 4, 4, false},
|
||||||
{"", 0, 10, 1, 1},
|
{"", 0, 10, 4, 4, false},
|
||||||
{"gitea", 0, 10, 0, 0},
|
{"gitea", 0, 10, 0, 0, false},
|
||||||
{"test", 0, 10, 1, 1},
|
{"test", 0, 10, 1, 1, false},
|
||||||
{"test", 1, 10, 1, 0},
|
{"test", 1, 10, 1, 0, false},
|
||||||
|
{"almost.similar", 0, 0, 3, 3, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
req := NewRequestWithBody(t, "PUT", url, createPackage(packageName, "1.0.99")).
|
fakePackages := []string{
|
||||||
AddBasicAuth(user.Name)
|
packageName,
|
||||||
MakeRequest(t, req, http.StatusCreated)
|
"almost.similar.dependency",
|
||||||
|
"almost.similar",
|
||||||
|
"almost.similar.dependant",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fakePackageName := range fakePackages {
|
||||||
|
req := NewRequestWithBody(t, "PUT", url, createPackage(fakePackageName, "1.0.99")).
|
||||||
|
AddBasicAuth(user.Name)
|
||||||
|
MakeRequest(t, req, http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("v2", func(t *testing.T) {
|
t.Run("v2", func(t *testing.T) {
|
||||||
t.Run("Search()", func(t *testing.T) {
|
t.Run("Search()", func(t *testing.T) {
|
||||||
|
@ -493,6 +504,63 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Packages()", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
t.Run("substringof", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
for i, c := range cases {
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages()?$filter=substringof('%s',tolower(Id))&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)).
|
||||||
|
AddBasicAuth(user.Name)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
var result FeedResponse
|
||||||
|
decodeXML(t, resp, &result)
|
||||||
|
|
||||||
|
assert.Equal(t, c.ExpectedTotal, result.Count, "case %d: unexpected total hits", i)
|
||||||
|
assert.Len(t, result.Entries, c.ExpectedResults, "case %d: unexpected result count", i)
|
||||||
|
|
||||||
|
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)).
|
||||||
|
AddBasicAuth(user.Name)
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
assert.Equal(t, strconv.FormatInt(c.ExpectedTotal, 10), resp.Body.String(), "case %d: unexpected total hits", i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("IdEq", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
for i, c := range cases {
|
||||||
|
if c.Query == "" {
|
||||||
|
// Ignore the `tolower(Id) eq ''` as it's unlikely to happen
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/Packages()?$filter=(tolower(Id) eq '%s')&$skip=%d&$top=%d", url, c.Query, c.Skip, c.Take)).
|
||||||
|
AddBasicAuth(user.Name)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
var result FeedResponse
|
||||||
|
decodeXML(t, resp, &result)
|
||||||
|
|
||||||
|
expectedCount := 0
|
||||||
|
if c.ExpectedExactMatch {
|
||||||
|
expectedCount = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, int64(expectedCount), result.Count, "case %d: unexpected total hits", i)
|
||||||
|
assert.Len(t, result.Entries, expectedCount, "case %d: unexpected result count", i)
|
||||||
|
|
||||||
|
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)).
|
||||||
|
AddBasicAuth(user.Name)
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
assert.Equal(t, strconv.FormatInt(int64(expectedCount), 10), resp.Body.String(), "case %d: unexpected total hits", i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Next", func(t *testing.T) {
|
t.Run("Next", func(t *testing.T) {
|
||||||
req := NewRequest(t, "GET", fmt.Sprintf("%s/Search()?searchTerm='test'&$skip=0&$top=1", url)).
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/Search()?searchTerm='test'&$skip=0&$top=1", url)).
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
|
@ -550,9 +618,11 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, "1.0.99")).
|
for _, fakePackageName := range fakePackages {
|
||||||
AddBasicAuth(user.Name)
|
req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, fakePackageName, "1.0.99")).
|
||||||
MakeRequest(t, req, http.StatusNoContent)
|
AddBasicAuth(user.Name)
|
||||||
|
MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("RegistrationService", func(t *testing.T) {
|
t.Run("RegistrationService", func(t *testing.T) {
|
||||||
|
|
|
@ -42,7 +42,7 @@ func TestAPIRepoTags(t *testing.T) {
|
||||||
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.zip", tags[0].ZipballURL)
|
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.zip", tags[0].ZipballURL)
|
||||||
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.tar.gz", tags[0].TarballURL)
|
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.tar.gz", tags[0].TarballURL)
|
||||||
|
|
||||||
newTag := createNewTagUsingAPI(t, session, token, user.Name, repoName, "gitea/22", "", "nice!\nand some text")
|
newTag := createNewTagUsingAPI(t, token, user.Name, repoName, "gitea/22", "", "nice!\nand some text")
|
||||||
resp = MakeRequest(t, req, http.StatusOK)
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
DecodeJSON(t, resp, &tags)
|
DecodeJSON(t, resp, &tags)
|
||||||
assert.Len(t, tags, 2)
|
assert.Len(t, tags, 2)
|
||||||
|
@ -72,7 +72,7 @@ func TestAPIRepoTags(t *testing.T) {
|
||||||
MakeRequest(t, req, http.StatusNotFound)
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createNewTagUsingAPI(t *testing.T, session *TestSession, token, ownerName, repoName, name, target, msg string) *api.Tag {
|
func createNewTagUsingAPI(t *testing.T, token, ownerName, repoName, name, target, msg string) *api.Tag {
|
||||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags", ownerName, repoName)
|
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags", ownerName, repoName)
|
||||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateTagOption{
|
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateTagOption{
|
||||||
TagName: name,
|
TagName: name,
|
||||||
|
@ -96,7 +96,7 @@ func TestAPIGetTagArchiveDownloadCount(t *testing.T) {
|
||||||
repoName := "repo1"
|
repoName := "repo1"
|
||||||
tagName := "TagDownloadCount"
|
tagName := "TagDownloadCount"
|
||||||
|
|
||||||
createNewTagUsingAPI(t, session, token, user.Name, repoName, tagName, "", "")
|
createNewTagUsingAPI(t, token, user.Name, repoName, tagName, "", "")
|
||||||
|
|
||||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/%s?token=%s", user.Name, repoName, tagName, token)
|
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/%s?token=%s", user.Name, repoName, tagName, token)
|
||||||
|
|
||||||
|
|
|
@ -237,7 +237,7 @@ func (c *compareDump) assertLoadFiles(beforeFilename, afterFilename string, t re
|
||||||
//
|
//
|
||||||
// Given []Something{} create afterPtr, beforePtr []*Something{}
|
// Given []Something{} create afterPtr, beforePtr []*Something{}
|
||||||
//
|
//
|
||||||
sliceType := reflect.SliceOf(reflect.PtrTo(t.Elem()))
|
sliceType := reflect.SliceOf(reflect.PointerTo(t.Elem()))
|
||||||
beforeSlice := reflect.MakeSlice(sliceType, 0, 10)
|
beforeSlice := reflect.MakeSlice(sliceType, 0, 10)
|
||||||
beforePtr = reflect.New(beforeSlice.Type())
|
beforePtr = reflect.New(beforeSlice.Type())
|
||||||
beforePtr.Elem().Set(beforeSlice)
|
beforePtr.Elem().Set(beforeSlice)
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestGPGGit(t *testing.T) {
|
||||||
defer os.Setenv("GNUPGHOME", oldGNUPGHome)
|
defer os.Setenv("GNUPGHOME", oldGNUPGHome)
|
||||||
|
|
||||||
// Need to create a root key
|
// Need to create a root key
|
||||||
rootKeyPair, err := importTestingKey(tmpDir, "gitea", "gitea@fake.local")
|
rootKeyPair, err := importTestingKey()
|
||||||
if !assert.NoError(t, err, "importTestingKey") {
|
if !assert.NoError(t, err, "importTestingKey") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -263,7 +263,7 @@ func TestGPGGit(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func crudActionCreateFile(t *testing.T, ctx APITestContext, user *user_model.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
|
func crudActionCreateFile(_ *testing.T, ctx APITestContext, user *user_model.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
|
||||||
return doAPICreateFile(ctx, path, &api.CreateFileOptions{
|
return doAPICreateFile(ctx, path, &api.CreateFileOptions{
|
||||||
FileOptions: api.FileOptions{
|
FileOptions: api.FileOptions{
|
||||||
BranchName: from,
|
BranchName: from,
|
||||||
|
@ -282,7 +282,7 @@ func crudActionCreateFile(t *testing.T, ctx APITestContext, user *user_model.Use
|
||||||
}, callback...)
|
}, callback...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func importTestingKey(tmpDir, name, email string) (*openpgp.Entity, error) {
|
func importTestingKey() (*openpgp.Entity, error) {
|
||||||
if _, _, err := process.GetManager().Exec("gpg --import tests/integration/private-testing.key", "gpg", "--import", "tests/integration/private-testing.key"); err != nil {
|
if _, _, err := process.GetManager().Exec("gpg --import tests/integration/private-testing.key", "gpg", "--import", "tests/integration/private-testing.key"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
23
tools/lint-go-gopls.sh
Executable file
23
tools/lint-go-gopls.sh
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname -- "${BASH_SOURCE[0]}")" && cd ..
|
||||||
|
|
||||||
|
IGNORE_PATTERNS=(
|
||||||
|
"is deprecated" # TODO: fix these
|
||||||
|
)
|
||||||
|
|
||||||
|
# lint all go files with 'gopls check' and look for lines starting with the
|
||||||
|
# current absolute path, indicating a error was found. This is neccessary
|
||||||
|
# because the tool does not set non-zero exit code when errors are found.
|
||||||
|
# ref: https://github.com/golang/go/issues/67078
|
||||||
|
ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}"));
|
||||||
|
NUM_ERRORS=$(echo -n "$ERROR_LINES" | wc -l)
|
||||||
|
|
||||||
|
if [ "$NUM_ERRORS" -eq "0" ]; then
|
||||||
|
exit 0;
|
||||||
|
else
|
||||||
|
echo "$ERROR_LINES"
|
||||||
|
echo "Found $NUM_ERRORS 'gopls check' errors"
|
||||||
|
exit 1;
|
||||||
|
fi
|
|
@ -9,6 +9,7 @@
|
||||||
.project-column {
|
.project-column {
|
||||||
background-color: var(--color-project-column-bg) !important;
|
background-color: var(--color-project-column-bg) !important;
|
||||||
border: 1px solid var(--color-secondary) !important;
|
border: 1px solid var(--color-secondary) !important;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
margin: 0 0.5rem !important;
|
margin: 0 0.5rem !important;
|
||||||
padding: 0.5rem !important;
|
padding: 0.5rem !important;
|
||||||
width: 320px;
|
width: 320px;
|
||||||
|
|
|
@ -35,22 +35,6 @@ Gitea's private styles use `g-` prefix.
|
||||||
.interact-bg:hover { background: var(--color-hover) !important; }
|
.interact-bg:hover { background: var(--color-hover) !important; }
|
||||||
.interact-bg:active { background: var(--color-active) !important; }
|
.interact-bg:active { background: var(--color-active) !important; }
|
||||||
|
|
||||||
/*
|
|
||||||
tw-hidden must win all other "display: xxx !important" classes to get the chance to "hide" an element.
|
|
||||||
do not use:
|
|
||||||
* "[hidden]" attribute: it's too weak, can not be applied to an element with "display: flex"
|
|
||||||
* ".hidden" class: it has been polluted by Fomantic UI in many cases
|
|
||||||
* inline style="display: none": it's difficult to tweak
|
|
||||||
* jQuery's show/hide/toggle: it can not show/hide elements with "display: xxx !important"
|
|
||||||
only use:
|
|
||||||
* this ".tw-hidden" class
|
|
||||||
* showElem/hideElem/toggleElem functions in "utils/dom.js"
|
|
||||||
*/
|
|
||||||
.tw-hidden.tw-hidden { display: none !important; }
|
|
||||||
|
|
||||||
/* proposed class from https://github.com/tailwindlabs/tailwindcss/pull/12128 */
|
|
||||||
.tw-break-anywhere { overflow-wrap: anywhere !important; }
|
|
||||||
|
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
/* double selector so it wins over .tw-flex (old .gt-df) etc */
|
/* double selector so it wins over .tw-flex (old .gt-df) etc */
|
||||||
.not-mobile.not-mobile {
|
.not-mobile.not-mobile {
|
||||||
|
|
|
@ -2489,6 +2489,7 @@ tbody.commit-list {
|
||||||
min-height: 12em;
|
min-height: 12em;
|
||||||
max-height: calc(100vh - 10.5rem);
|
max-height: calc(100vh - 10.5rem);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
tab-size: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-diff-data pre {
|
.comment-diff-data pre {
|
||||||
|
|
Loading…
Reference in a new issue