diff --git a/modules/label/parser.go b/modules/label/parser.go index 55bf570de6b95..2673cfdf15174 100644 --- a/modules/label/parser.go +++ b/modules/label/parser.go @@ -36,21 +36,17 @@ func (err ErrTemplateLoad) Error() string { // GetTemplateFile loads the label template file by given name, // then parses and returns a list of name-color pairs and optionally description. func GetTemplateFile(name string) ([]*Label, error) { - data, err := options.Labels(name + ".yaml") - if err == nil && len(data) > 0 { - return parseYamlFormat(name+".yaml", data) + // Always check if .yaml or .yml exists and prefer those + data, extension, err := options.Labels(name, ".yaml", ".yml", "") + if err != nil { + return nil, ErrTemplateLoad{name, fmt.Errorf("GetRepoInitFile: %w", err)} } - data, err = options.Labels(name + ".yml") - if err == nil && len(data) > 0 { + // because we only handle .yaml/.yml we can simply test if the extension is not empty + if len(extension) > 0 && len(data) > 0 { return parseYamlFormat(name+".yml", data) } - data, err = options.Labels(name) - if err != nil { - return nil, ErrTemplateLoad{name, fmt.Errorf("GetRepoInitFile: %w", err)} - } - return parseLegacyFormat(name, data) } diff --git a/modules/options/base.go b/modules/options/base.go index 7882ed008159a..7092a9160dd83 100644 --- a/modules/options/base.go +++ b/modules/options/base.go @@ -37,8 +37,8 @@ func License(name string) ([]byte, error) { } // Labels reads the content of a specific labels from static/bindata or custom path. -func Labels(name string) ([]byte, error) { - return fileFromOptionsDir("label", name) +func Labels(name string, exts ...string) ([]byte, string, error) { + return fileFromOptionsDirExtensions([]string{"label", name}, exts...) } // WalkLocales reads the content of a specific locale @@ -132,3 +132,20 @@ func readLocalFile(baseDirs []string, subDir string, elems ...string) ([]byte, e } return nil, os.ErrNotExist } + +func readLocalFileExtensions(baseDirs []string, subDir string, elems []string, extensions ...string) ([]byte, string, error) { + if len(extensions) == 0 { + extensions = append(extensions, "") + } + for _, localPath := range joinLocalPaths(baseDirs, subDir, elems...) { + for _, extension := range extensions { + data, err := os.ReadFile(localPath + extension) + if err == nil { + return data, extension, nil + } else if !os.IsNotExist(err) { + log.Error("Unable to read file %q. Error: %v", localPath, err) + } + } + } + return nil, "", os.ErrNotExist +} diff --git a/modules/options/dynamic.go b/modules/options/dynamic.go index 3d6261983f2de..2385ee80fbace 100644 --- a/modules/options/dynamic.go +++ b/modules/options/dynamic.go @@ -28,6 +28,11 @@ func fileFromOptionsDir(elems ...string) ([]byte, error) { return readLocalFile([]string{setting.CustomPath, setting.StaticRootPath}, "options", elems...) } +// fileFromOptionsDirExtensions is a helper to read files from custom or static path. +func fileFromOptionsDirExtensions(elems []string, extensions ...string) ([]byte, string, error) { + return readLocalFileExtensions([]string{setting.CustomPath, setting.StaticRootPath}, "options", elems, extensions...) +} + // IsDynamic will return false when using embedded data (-tags bindata) func IsDynamic() bool { return true diff --git a/modules/options/static.go b/modules/options/static.go index 0482dea6817ce..03e8b333b07cd 100644 --- a/modules/options/static.go +++ b/modules/options/static.go @@ -8,6 +8,7 @@ package options import ( "fmt" "io" + "os" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -66,6 +67,35 @@ func fileFromOptionsDir(elems ...string) ([]byte, error) { return io.ReadAll(f) } +// fileFromOptionsDir is a helper to read files from custom path or bindata. +func fileFromOptionsDirExtensions(elems []string, extensions ...string) ([]byte, string, error) { + // only try custom dir, no static dir + if data, extension, err := readLocalFileExtensions([]string{setting.CustomPath}, "options", elems, extensions...); err == nil { + return data, extension, nil + } + + if len(extensions) == 0 { + extensions = append(extensions, "") + } + + for _, extension := range extensions { + f, err := Assets.Open(util.PathJoinRelX(elems...)) + if err != nil { + if os.IsNotExist(err) { + continue + } + return nil, "", err + } + defer f.Close() + bs, err := io.ReadAll(f) + if err != nil { + return nil, "", err + } + return bs, extension, nil + } + return nil, "", os.ErrNotExist +} + func Asset(name string) ([]byte, error) { f, err := Assets.Open("/" + name) if err != nil { diff --git a/modules/repository/init.go b/modules/repository/init.go index f9a33cd4f68c9..32753bef61621 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -45,7 +45,18 @@ var ( func LoadRepoConfig() { // Load .gitignore and license files and readme templates. types := []string{"gitignore", "license", "readme", "label"} - typeFiles := make([][]string, 4) + + removeExtension := func(f string) string { + ext := strings.ToLower(filepath.Ext(f)) + if ext == ".yaml" || ext == ".yml" { + return f[:len(f)-len(ext)] + } + return f + } + + labelTemplatesFiles := []string{} + typeFiles := []*[]string{&Gitignores, &Licenses, &Readmes, &labelTemplatesFiles} + for i, t := range types { files, err := options.Dir(t) if err != nil { @@ -53,10 +64,7 @@ func LoadRepoConfig() { } if t == "label" { for i, f := range files { - ext := strings.ToLower(filepath.Ext(f)) - if ext == ".yaml" || ext == ".yml" { - files[i] = f[:len(f)-len(ext)] - } + files[i] = removeExtension(f) } } customPath := path.Join(setting.CustomPath, "options", t) @@ -71,26 +79,22 @@ func LoadRepoConfig() { } for _, f := range customFiles { + if t == "label" { + f = removeExtension(f) + } + if !util.SliceContainsString(files, f, true) { files = append(files, f) } } } - typeFiles[i] = files + sort.Strings(files) + *typeFiles[i] = files } - Gitignores = typeFiles[0] - Licenses = typeFiles[1] - Readmes = typeFiles[2] - LabelTemplatesFiles := typeFiles[3] - sort.Strings(Gitignores) - sort.Strings(Licenses) - sort.Strings(Readmes) - sort.Strings(LabelTemplatesFiles) - // Load label templates LabelTemplates = make(map[string]string) - for _, templateFile := range LabelTemplatesFiles { + for _, templateFile := range labelTemplatesFiles { labels, err := label.LoadFormatted(templateFile) if err != nil { log.Error("Failed to load labels: %v", err)