Compare commits

..

3 Commits

Author SHA1 Message Date
Giteabot
f1c5d33d3e
Fix storage path logic especially for relative paths (#26441) (#26481)
Backport #26441 by @lunny

This PR rewrites the function `getStorage` and make it more clear.

Include tests from #26435, thanks @earl-warren

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Earl Warren <contact@earl-warren.org>
2023-08-13 22:38:18 -04:00
Giteabot
acc0fd22d8
Add ThreadID parameter for Telegram webhooks (#25996) (#26480)
Backport #25996

Telegram has recently implemented threads (channels) for group chats.

Co-authored-by: Earl Warren <109468362+earl-warren@users.noreply.github.com>
Co-authored-by: neveraskedtoexist <matikot415@gmail.com>
2023-08-14 08:55:17 +08:00
Giteabot
fe1b11b639
Close stdout correctly for "git blame" (#26470) (#26473)
Backport #26470 by @wxiaoguang

Close stdout correctly for "git blame", otherwise the failed "git blame"
would cause the request hanging forever.

And "os.Stderr" should never (seldom) be used as git command's stderr
(there seems some similar problems in code, they could be fixed later).

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2023-08-13 16:00:28 +08:00
8 changed files with 327 additions and 111 deletions

View File

@ -5,11 +5,14 @@ package git
import ( import (
"bufio" "bufio"
"bytes"
"context" "context"
"fmt" "fmt"
"io" "io"
"os" "os"
"regexp" "regexp"
"code.gitea.io/gitea/modules/log"
) )
// BlamePart represents block of blame - continuous lines with one sha // BlamePart represents block of blame - continuous lines with one sha
@ -115,15 +118,19 @@ func CreateBlameReader(ctx context.Context, repoPath, commitID, file string) (*B
done := make(chan error, 1) done := make(chan error, 1)
go func(cmd *Command, dir string, stdout io.WriteCloser, done chan error) { go func(cmd *Command, dir string, stdout io.WriteCloser, done chan error) {
if err := cmd.Run(&RunOpts{ stderr := bytes.Buffer{}
// TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close"
err := cmd.Run(&RunOpts{
UseContextTimeout: true, UseContextTimeout: true,
Dir: dir, Dir: dir,
Stdout: stdout, Stdout: stdout,
Stderr: os.Stderr, Stderr: &stderr,
}); err == nil { })
stdout.Close()
}
done <- err done <- err
_ = stdout.Close()
if err != nil {
log.Error("Error running git blame (dir: %v): %v, stderr: %v", repoPath, err, stderr.String())
}
}(cmd, repoPath, stdout, done) }(cmd, repoPath, stdout, done)
bufferedReader := bufio.NewReader(reader) bufferedReader := bufio.NewReader(reader)

View File

@ -91,134 +91,172 @@ func getStorage(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (*S
return nil, errors.New("no name for storage") return nil, errors.New("no name for storage")
} }
var targetSec ConfigSection targetSec, tp, err := getStorageTargetSection(rootCfg, name, typ, sec)
// check typ first if err != nil {
if typ != "" { return nil, err
var err error
targetSec, err = rootCfg.GetSection(storageSectionName + "." + typ)
if err != nil {
if !IsValidStorageType(StorageType(typ)) {
return nil, fmt.Errorf("get section via storage type %q failed: %v", typ, err)
}
}
if targetSec != nil {
targetType := targetSec.Key("STORAGE_TYPE").String()
if targetType == "" {
if !IsValidStorageType(StorageType(typ)) {
return nil, fmt.Errorf("unknow storage type %q", typ)
}
targetSec.Key("STORAGE_TYPE").SetValue(typ)
} else if !IsValidStorageType(StorageType(targetType)) {
return nil, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ)
}
}
} }
if targetSec == nil && sec != nil { overrideSec := getStorageOverrideSection(rootCfg, targetSec, sec, tp, name)
secTyp := sec.Key("STORAGE_TYPE").String()
if IsValidStorageType(StorageType(secTyp)) {
targetSec = sec
} else if secTyp != "" {
targetSec, _ = rootCfg.GetSection(storageSectionName + "." + secTyp)
}
}
targetSecIsStoragename := false targetType := targetSec.Key("STORAGE_TYPE").String()
storageNameSec, _ := rootCfg.GetSection(storageSectionName + "." + name) switch targetType {
if targetSec == nil { case string(LocalStorageType):
targetSec = storageNameSec return getStorageForLocal(targetSec, overrideSec, tp, name)
targetSecIsStoragename = storageNameSec != nil case string(MinioStorageType):
return getStorageForMinio(targetSec, overrideSec, tp, name)
default:
return nil, fmt.Errorf("unsupported storage type %q", targetType)
} }
}
if targetSec == nil { type targetSecType int
targetSec = getDefaultStorageSection(rootCfg)
} else { const (
targetType := targetSec.Key("STORAGE_TYPE").String() targetSecIsTyp targetSecType = iota // target section is [storage.type] which the type from parameter
switch { targetSecIsStorage // target section is [storage]
case targetType == "": targetSecIsDefault // target section is the default value
if targetSec != storageNameSec && storageNameSec != nil { targetSecIsStorageWithName // target section is [storage.name]
targetSec = storageNameSec targetSecIsSec // target section is from the name seciont [name]
targetSecIsStoragename = true )
if targetSec.Key("STORAGE_TYPE").String() == "" {
return nil, fmt.Errorf("storage section %s.%s has no STORAGE_TYPE", storageSectionName, name) func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetSecType, error) {
} targetSec, err := rootCfg.GetSection(storageSectionName + "." + typ)
} else { if err != nil {
if targetSec.Key("PATH").String() == "" { if !IsValidStorageType(StorageType(typ)) {
targetSec = getDefaultStorageSection(rootCfg) return nil, 0, fmt.Errorf("get section via storage type %q failed: %v", typ, err)
} else {
targetSec.Key("STORAGE_TYPE").SetValue("local")
}
}
default:
newTargetSec, _ := rootCfg.GetSection(storageSectionName + "." + targetType)
if newTargetSec == nil {
if !IsValidStorageType(StorageType(targetType)) {
return nil, fmt.Errorf("invalid storage section %s.%q", storageSectionName, targetType)
}
} else {
targetSec = newTargetSec
if IsValidStorageType(StorageType(targetType)) {
tp := targetSec.Key("STORAGE_TYPE").String()
if tp == "" {
targetSec.Key("STORAGE_TYPE").SetValue(targetType)
}
}
}
} }
// if typ is a valid storage type, but there is no [storage.local] or [storage.minio] section
// it's not an error
return nil, 0, nil
} }
targetType := targetSec.Key("STORAGE_TYPE").String() targetType := targetSec.Key("STORAGE_TYPE").String()
if !IsValidStorageType(StorageType(targetType)) { if targetType == "" {
return nil, fmt.Errorf("invalid storage type %q", targetType) if !IsValidStorageType(StorageType(typ)) {
return nil, 0, fmt.Errorf("unknow storage type %q", typ)
}
targetSec.Key("STORAGE_TYPE").SetValue(typ)
} else if !IsValidStorageType(StorageType(targetType)) {
return nil, 0, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ)
} }
// extra config section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible return targetSec, targetSecIsTyp, nil
extraConfigSec := sec }
if extraConfigSec == nil {
extraConfigSec = storageNameSec func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (ConfigSection, targetSecType, error) {
// check typ first
if typ == "" {
if sec != nil { // check sec's type secondly
typ = sec.Key("STORAGE_TYPE").String()
if IsValidStorageType(StorageType(typ)) {
if targetSec, _ := rootCfg.GetSection(storageSectionName + "." + typ); targetSec == nil {
return sec, targetSecIsSec, nil
}
}
}
} }
var storage Storage if typ != "" {
storage.Type = StorageType(targetType) targetSec, tp, err := getStorageSectionByType(rootCfg, typ)
if targetSec != nil || err != nil {
switch targetType { return targetSec, tp, err
case string(LocalStorageType):
targetPath := ConfigSectionKeyString(targetSec, "PATH", "")
if targetPath == "" {
targetPath = AppDataPath
} else if !filepath.IsAbs(targetPath) {
targetPath = filepath.Join(AppDataPath, targetPath)
} }
}
var fallbackPath string // check stoarge name thirdly
if targetSecIsStoragename { targetSec, _ := rootCfg.GetSection(storageSectionName + "." + name)
fallbackPath = targetPath if targetSec != nil {
} else { targetType := targetSec.Key("STORAGE_TYPE").String()
fallbackPath = filepath.Join(targetPath, name) switch {
} case targetType == "":
if targetSec.Key("PATH").String() == "" { // both storage type and path are empty, use default
return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil
}
if extraConfigSec == nil { targetSec.Key("STORAGE_TYPE").SetValue("local")
storage.Path = fallbackPath default:
} else { targetSec, tp, err := getStorageSectionByType(rootCfg, targetType)
storage.Path = ConfigSectionKeyString(extraConfigSec, "PATH", fallbackPath) if targetSec != nil || err != nil {
if !filepath.IsAbs(storage.Path) { return targetSec, tp, err
storage.Path = filepath.Join(targetPath, storage.Path)
} }
} }
case string(MinioStorageType): return targetSec, targetSecIsStorageWithName, nil
if err := targetSec.MapTo(&storage.MinioConfig); err != nil { }
return nil, fmt.Errorf("map minio config failed: %v", err)
return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil
}
// getStorageOverrideSection override section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible
func getStorageOverrideSection(rootConfig ConfigProvider, targetSec, sec ConfigSection, targetSecType targetSecType, name string) ConfigSection {
if targetSecType == targetSecIsSec {
return nil
}
if sec != nil {
return sec
}
if targetSecType != targetSecIsStorageWithName {
nameSec, _ := rootConfig.GetSection(storageSectionName + "." + name)
return nameSec
}
return nil
}
func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) {
storage := Storage{
Type: StorageType(targetSec.Key("STORAGE_TYPE").String()),
}
targetPath := ConfigSectionKeyString(targetSec, "PATH", "")
var fallbackPath string
if targetPath == "" { // no path
fallbackPath = filepath.Join(AppDataPath, name)
} else {
if tp == targetSecIsStorage || tp == targetSecIsDefault {
fallbackPath = filepath.Join(targetPath, name)
} else {
fallbackPath = targetPath
} }
if !filepath.IsAbs(fallbackPath) {
fallbackPath = filepath.Join(AppDataPath, fallbackPath)
}
}
storage.MinioConfig.BasePath = name + "/" if overrideSec == nil { // no override section
storage.Path = fallbackPath
if extraConfigSec != nil { } else {
storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(extraConfigSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect) storage.Path = ConfigSectionKeyString(overrideSec, "PATH", "")
storage.MinioConfig.BasePath = ConfigSectionKeyString(extraConfigSec, "MINIO_BASE_PATH", storage.MinioConfig.BasePath) if storage.Path == "" { // overrideSec has no path
storage.MinioConfig.Bucket = ConfigSectionKeyString(extraConfigSec, "MINIO_BUCKET", storage.MinioConfig.Bucket) storage.Path = fallbackPath
} else if !filepath.IsAbs(storage.Path) {
if targetPath == "" {
storage.Path = filepath.Join(AppDataPath, storage.Path)
} else {
storage.Path = filepath.Join(targetPath, storage.Path)
}
} }
} }
return &storage, nil return &storage, nil
} }
func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) {
var storage Storage
storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String())
if err := targetSec.MapTo(&storage.MinioConfig); err != nil {
return nil, fmt.Errorf("map minio config failed: %v", err)
}
if storage.MinioConfig.BasePath == "" {
storage.MinioConfig.BasePath = name + "/"
}
if overrideSec != nil {
storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(overrideSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect)
storage.MinioConfig.BasePath = ConfigSectionKeyString(overrideSec, "MINIO_BASE_PATH", storage.MinioConfig.BasePath)
storage.MinioConfig.Bucket = ConfigSectionKeyString(overrideSec, "MINIO_BUCKET", storage.MinioConfig.Bucket)
}
return &storage, nil
}

View File

@ -27,12 +27,15 @@ MINIO_BUCKET = gitea-storage
assert.NoError(t, loadAttachmentFrom(cfg)) assert.NoError(t, loadAttachmentFrom(cfg))
assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket)
assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
assert.NoError(t, loadLFSFrom(cfg)) assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket) assert.EqualValues(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket)
assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
assert.NoError(t, loadAvatarsFrom(cfg)) assert.NoError(t, loadAvatarsFrom(cfg))
assert.EqualValues(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket) assert.EqualValues(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket)
assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath)
} }
func Test_getStorageUseOtherNameAsType(t *testing.T) { func Test_getStorageUseOtherNameAsType(t *testing.T) {
@ -49,9 +52,11 @@ MINIO_BUCKET = gitea-storage
assert.NoError(t, loadAttachmentFrom(cfg)) assert.NoError(t, loadAttachmentFrom(cfg))
assert.EqualValues(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket) assert.EqualValues(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket)
assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath)
assert.NoError(t, loadLFSFrom(cfg)) assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket) assert.EqualValues(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket)
assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
} }
func Test_getStorageInheritStorageType(t *testing.T) { func Test_getStorageInheritStorageType(t *testing.T) {
@ -117,6 +122,9 @@ func Test_getStorageInheritStorageTypeLocal(t *testing.T) {
[storage] [storage]
STORAGE_TYPE = local STORAGE_TYPE = local
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/appdata/attachments"},
{loadLFSFrom, &LFS.Storage, "/appdata/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/appdata/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/appdata/packages"}, {loadPackagesFrom, &Packages.Storage, "/appdata/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/appdata/repo-archive"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/appdata/repo-archive"},
{loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"},
@ -131,6 +139,9 @@ func Test_getStorageInheritStorageTypeLocalPath(t *testing.T) {
STORAGE_TYPE = local STORAGE_TYPE = local
PATH = /data/gitea PATH = /data/gitea
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/data/gitea/attachments"},
{loadLFSFrom, &LFS.Storage, "/data/gitea/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/data/gitea/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/data/gitea/packages"}, {loadPackagesFrom, &Packages.Storage, "/data/gitea/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/repo-archive"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/repo-archive"},
{loadActionsFrom, &Actions.LogStorage, "/data/gitea/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/data/gitea/actions_log"},
@ -145,6 +156,9 @@ func Test_getStorageInheritStorageTypeLocalRelativePath(t *testing.T) {
STORAGE_TYPE = local STORAGE_TYPE = local
PATH = storages PATH = storages
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/appdata/storages/attachments"},
{loadLFSFrom, &LFS.Storage, "/appdata/storages/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/appdata/storages/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/appdata/storages/packages"}, {loadPackagesFrom, &Packages.Storage, "/appdata/storages/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/appdata/storages/repo-archive"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/appdata/storages/repo-archive"},
{loadActionsFrom, &Actions.LogStorage, "/appdata/storages/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/appdata/storages/actions_log"},
@ -162,6 +176,9 @@ PATH = /data/gitea
[repo-archive] [repo-archive]
PATH = /data/gitea/the-archives-dir PATH = /data/gitea/the-archives-dir
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/data/gitea/attachments"},
{loadLFSFrom, &LFS.Storage, "/data/gitea/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/data/gitea/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/data/gitea/packages"}, {loadPackagesFrom, &Packages.Storage, "/data/gitea/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/the-archives-dir"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/the-archives-dir"},
{loadActionsFrom, &Actions.LogStorage, "/data/gitea/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/data/gitea/actions_log"},
@ -178,6 +195,9 @@ PATH = /data/gitea
[repo-archive] [repo-archive]
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/data/gitea/attachments"},
{loadLFSFrom, &LFS.Storage, "/data/gitea/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/data/gitea/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/data/gitea/packages"}, {loadPackagesFrom, &Packages.Storage, "/data/gitea/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/repo-archive"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/repo-archive"},
{loadActionsFrom, &Actions.LogStorage, "/data/gitea/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/data/gitea/actions_log"},
@ -195,6 +215,9 @@ PATH = /data/gitea
[repo-archive] [repo-archive]
PATH = the-archives-dir PATH = the-archives-dir
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/data/gitea/attachments"},
{loadLFSFrom, &LFS.Storage, "/data/gitea/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/data/gitea/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/data/gitea/packages"}, {loadPackagesFrom, &Packages.Storage, "/data/gitea/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/the-archives-dir"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/the-archives-dir"},
{loadActionsFrom, &Actions.LogStorage, "/data/gitea/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/data/gitea/actions_log"},
@ -209,6 +232,9 @@ func Test_getStorageInheritStorageTypeLocalPathOverride3(t *testing.T) {
STORAGE_TYPE = local STORAGE_TYPE = local
PATH = /data/gitea/archives PATH = /data/gitea/archives
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/appdata/attachments"},
{loadLFSFrom, &LFS.Storage, "/appdata/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/appdata/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/appdata/packages"}, {loadPackagesFrom, &Packages.Storage, "/appdata/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/archives"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/archives"},
{loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"},
@ -217,6 +243,23 @@ PATH = /data/gitea/archives
}) })
} }
func Test_getStorageInheritStorageTypeLocalPathOverride3_5(t *testing.T) {
testLocalStoragePath(t, "/appdata", `
[storage.repo-archive]
STORAGE_TYPE = local
PATH = a-relative-path
`, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/appdata/attachments"},
{loadLFSFrom, &LFS.Storage, "/appdata/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/appdata/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/appdata/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/appdata/a-relative-path"},
{loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"},
{loadAvatarsFrom, &Avatar.Storage, "/appdata/avatars"},
{loadRepoAvatarFrom, &RepoAvatar.Storage, "/appdata/repo-avatars"},
})
}
func Test_getStorageInheritStorageTypeLocalPathOverride4(t *testing.T) { func Test_getStorageInheritStorageTypeLocalPathOverride4(t *testing.T) {
testLocalStoragePath(t, "/appdata", ` testLocalStoragePath(t, "/appdata", `
[storage.repo-archive] [storage.repo-archive]
@ -226,6 +269,9 @@ PATH = /data/gitea/archives
[repo-archive] [repo-archive]
PATH = /tmp/gitea/archives PATH = /tmp/gitea/archives
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/appdata/attachments"},
{loadLFSFrom, &LFS.Storage, "/appdata/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/appdata/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/appdata/packages"}, {loadPackagesFrom, &Packages.Storage, "/appdata/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/tmp/gitea/archives"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/tmp/gitea/archives"},
{loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"},
@ -242,6 +288,9 @@ PATH = /data/gitea/archives
[repo-archive] [repo-archive]
`, []testLocalStoragePathCase{ `, []testLocalStoragePathCase{
{loadAttachmentFrom, &Attachment.Storage, "/appdata/attachments"},
{loadLFSFrom, &LFS.Storage, "/appdata/lfs"},
{loadActionsFrom, &Actions.ArtifactStorage, "/appdata/actions_artifacts"},
{loadPackagesFrom, &Packages.Storage, "/appdata/packages"}, {loadPackagesFrom, &Packages.Storage, "/appdata/packages"},
{loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/archives"}, {loadRepoArchiveFrom, &RepoArchive.Storage, "/data/gitea/archives"},
{loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"}, {loadActionsFrom, &Actions.LogStorage, "/appdata/actions_log"},
@ -249,3 +298,117 @@ PATH = /data/gitea/archives
{loadRepoAvatarFrom, &RepoAvatar.Storage, "/appdata/repo-avatars"}, {loadRepoAvatarFrom, &RepoAvatar.Storage, "/appdata/repo-avatars"},
}) })
} }
func Test_getStorageInheritStorageTypeLocalPathOverride72(t *testing.T) {
testLocalStoragePath(t, "/appdata", `
[repo-archive]
STORAGE_TYPE = local
PATH = archives
`, []testLocalStoragePathCase{
{loadRepoArchiveFrom, &RepoArchive.Storage, "/appdata/archives"},
})
}
func Test_getStorageConfiguration20(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repo-archive]
STORAGE_TYPE = my_storage
PATH = archives
`)
assert.NoError(t, err)
assert.Error(t, loadRepoArchiveFrom(cfg))
}
func Test_getStorageConfiguration21(t *testing.T) {
testLocalStoragePath(t, "/appdata", `
[storage.repo-archive]
`, []testLocalStoragePathCase{
{loadRepoArchiveFrom, &RepoArchive.Storage, "/appdata/repo-archive"},
})
}
func Test_getStorageConfiguration22(t *testing.T) {
testLocalStoragePath(t, "/appdata", `
[storage.repo-archive]
PATH = archives
`, []testLocalStoragePathCase{
{loadRepoArchiveFrom, &RepoArchive.Storage, "/appdata/archives"},
})
}
func Test_getStorageConfiguration23(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repo-archive]
STORAGE_TYPE = minio
MINIO_ACCESS_KEY_ID = my_access_key
MINIO_SECRET_ACCESS_KEY = my_secret_key
`)
assert.NoError(t, err)
_, err = getStorage(cfg, "", "", nil)
assert.Error(t, err)
assert.NoError(t, loadRepoArchiveFrom(cfg))
cp := RepoArchive.Storage.ToShadowCopy()
assert.EqualValues(t, "******", cp.MinioConfig.AccessKeyID)
assert.EqualValues(t, "******", cp.MinioConfig.SecretAccessKey)
}
func Test_getStorageConfiguration24(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repo-archive]
STORAGE_TYPE = my_archive
[storage.my_archive]
; unsupported, storage type should be defined explicitly
PATH = archives
`)
assert.NoError(t, err)
assert.Error(t, loadRepoArchiveFrom(cfg))
}
func Test_getStorageConfiguration25(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repo-archive]
STORAGE_TYPE = my_archive
[storage.my_archive]
; unsupported, storage type should be known type
STORAGE_TYPE = unknown // should be local or minio
PATH = archives
`)
assert.NoError(t, err)
assert.Error(t, loadRepoArchiveFrom(cfg))
}
func Test_getStorageConfiguration26(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[repo-archive]
STORAGE_TYPE = minio
MINIO_ACCESS_KEY_ID = my_access_key
MINIO_SECRET_ACCESS_KEY = my_secret_key
; wrong configuration
MINIO_USE_SSL = abc
`)
assert.NoError(t, err)
// assert.Error(t, loadRepoArchiveFrom(cfg))
// FIXME: this should return error but now ini package's MapTo() doesn't check type
assert.NoError(t, loadRepoArchiveFrom(cfg))
}
func Test_getStorageConfiguration27(t *testing.T) {
cfg, err := NewConfigProviderFromData(`
[storage.repo-archive]
STORAGE_TYPE = minio
MINIO_ACCESS_KEY_ID = my_access_key
MINIO_SECRET_ACCESS_KEY = my_secret_key
MINIO_USE_SSL = true
`)
assert.NoError(t, err)
assert.NoError(t, loadRepoArchiveFrom(cfg))
assert.EqualValues(t, "my_access_key", RepoArchive.Storage.MinioConfig.AccessKeyID)
assert.EqualValues(t, "my_secret_key", RepoArchive.Storage.MinioConfig.SecretAccessKey)
assert.EqualValues(t, true, RepoArchive.Storage.MinioConfig.UseSSL)
assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath)
}

View File

@ -2284,6 +2284,7 @@ settings.tags.protection.none = There are no protected tags.
settings.tags.protection.pattern.description = You can use a single name or a glob pattern or regular expression to match multiple tags. Read more in the <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/protected-tags/">protected tags guide</a>. settings.tags.protection.pattern.description = You can use a single name or a glob pattern or regular expression to match multiple tags. Read more in the <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/protected-tags/">protected tags guide</a>.
settings.bot_token = Bot Token settings.bot_token = Bot Token
settings.chat_id = Chat ID settings.chat_id = Chat ID
settings.thread_id = Thread ID
settings.matrix.homeserver_url = Homeserver URL settings.matrix.homeserver_url = Homeserver URL
settings.matrix.room_id = Room ID settings.matrix.room_id = Room ID
settings.matrix.message_type = Message Type settings.matrix.message_type = Message Type

View File

@ -425,12 +425,13 @@ func telegramHookParams(ctx *context.Context) webhookParams {
return webhookParams{ return webhookParams{
Type: webhook_module.TELEGRAM, Type: webhook_module.TELEGRAM,
URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)), URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&message_thread_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID), url.QueryEscape(form.ThreadID)),
ContentType: webhook.ContentTypeJSON, ContentType: webhook.ContentTypeJSON,
WebhookForm: form.WebhookForm, WebhookForm: form.WebhookForm,
Meta: &webhook_service.TelegramMeta{ Meta: &webhook_service.TelegramMeta{
BotToken: form.BotToken, BotToken: form.BotToken,
ChatID: form.ChatID, ChatID: form.ChatID,
ThreadID: form.ThreadID,
}, },
} }
} }

View File

@ -352,6 +352,7 @@ func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) b
type NewTelegramHookForm struct { type NewTelegramHookForm struct {
BotToken string `binding:"Required"` BotToken string `binding:"Required"`
ChatID string `binding:"Required"` ChatID string `binding:"Required"`
ThreadID string
WebhookForm WebhookForm
} }

View File

@ -28,6 +28,7 @@ type (
TelegramMeta struct { TelegramMeta struct {
BotToken string `json:"bot_token"` BotToken string `json:"bot_token"`
ChatID string `json:"chat_id"` ChatID string `json:"chat_id"`
ThreadID string `json:"thread_id"`
} }
) )

View File

@ -10,6 +10,10 @@
<label for="chat_id">{{.locale.Tr "repo.settings.chat_id"}}</label> <label for="chat_id">{{.locale.Tr "repo.settings.chat_id"}}</label>
<input id="chat_id" name="chat_id" type="text" value="{{.TelegramHook.ChatID}}" required> <input id="chat_id" name="chat_id" type="text" value="{{.TelegramHook.ChatID}}" required>
</div> </div>
<div class="field {{if .Err_ThreadID}}error{{end}}">
<label for="thread_id">{{.locale.Tr "repo.settings.thread_id"}}</label>
<input id="thread_id" name="thread_id" type="text" value="{{.TelegramHook.ThreadID}}">
</div>
{{template "repo/settings/webhook/settings" .}} {{template "repo/settings/webhook/settings" .}}
</form> </form>
{{end}} {{end}}