aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorJakobDev <jakobdev@gmx.de>2023-03-28 20:22:07 +0200
committerGitHub <noreply@github.com>2023-03-28 14:22:07 -0400
commitf384b13f1cd44be3a87df5553a0099390dacd010 (patch)
tree08b2744df3a8792ef2f50e4d559d55e4a349c198 /modules
parent5cd1d6c93ba9b8399f826e671b8940eb5294b872 (diff)
downloadgitea-f384b13f1cd44be3a87df5553a0099390dacd010.tar.gz
gitea-f384b13f1cd44be3a87df5553a0099390dacd010.zip
Implement Issue Config (#20956)
Closes #20955 This PR adds the possibility to disable blank Issues, when the Repo has templates. This can be done by creating the file `.gitea/issue_config.yaml` with the content `blank_issues_enabled` in the Repo.
Diffstat (limited to 'modules')
-rw-r--r--modules/context/repo.go114
-rw-r--r--modules/structs/issue.go16
2 files changed, 130 insertions, 0 deletions
diff --git a/modules/context/repo.go b/modules/context/repo.go
index b83caf4e4b..820e756fbd 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -8,6 +8,7 @@ import (
"context"
"fmt"
"html"
+ "io"
"net/http"
"net/url"
"path"
@@ -33,6 +34,7 @@ import (
asymkey_service "code.gitea.io/gitea/services/asymkey"
"github.com/editorconfig/editorconfig-core-go/v2"
+ "gopkg.in/yaml.v3"
)
// IssueTemplateDirCandidates issue templates directory
@@ -47,6 +49,13 @@ var IssueTemplateDirCandidates = []string{
".gitlab/issue_template",
}
+var IssueConfigCandidates = []string{
+ ".gitea/ISSUE_TEMPLATE/config",
+ ".gitea/issue_template/config",
+ ".github/ISSUE_TEMPLATE/config",
+ ".github/issue_template/config",
+}
+
// PullRequest contains information to make a pull request
type PullRequest struct {
BaseRepo *repo_model.Repository
@@ -1088,3 +1097,108 @@ func (ctx *Context) IssueTemplatesErrorsFromDefaultBranch() ([]*api.IssueTemplat
}
return issueTemplates, invalidFiles
}
+
+func GetDefaultIssueConfig() api.IssueConfig {
+ return api.IssueConfig{
+ BlankIssuesEnabled: true,
+ ContactLinks: make([]api.IssueConfigContactLink, 0),
+ }
+}
+
+// GetIssueConfig loads the given issue config file.
+// It never returns a nil config.
+func (r *Repository) GetIssueConfig(path string, commit *git.Commit) (api.IssueConfig, error) {
+ if r.GitRepo == nil {
+ return GetDefaultIssueConfig(), nil
+ }
+
+ var err error
+
+ treeEntry, err := commit.GetTreeEntryByPath(path)
+ if err != nil {
+ return GetDefaultIssueConfig(), err
+ }
+
+ reader, err := treeEntry.Blob().DataAsync()
+ if err != nil {
+ log.Debug("DataAsync: %v", err)
+ return GetDefaultIssueConfig(), nil
+ }
+
+ defer reader.Close()
+
+ configContent, err := io.ReadAll(reader)
+ if err != nil {
+ return GetDefaultIssueConfig(), err
+ }
+
+ issueConfig := api.IssueConfig{}
+ if err := yaml.Unmarshal(configContent, &issueConfig); err != nil {
+ return GetDefaultIssueConfig(), err
+ }
+
+ for pos, link := range issueConfig.ContactLinks {
+ if link.Name == "" {
+ return GetDefaultIssueConfig(), fmt.Errorf("contact_link at position %d is missing name key", pos+1)
+ }
+
+ if link.URL == "" {
+ return GetDefaultIssueConfig(), fmt.Errorf("contact_link at position %d is missing url key", pos+1)
+ }
+
+ if link.About == "" {
+ return GetDefaultIssueConfig(), fmt.Errorf("contact_link at position %d is missing about key", pos+1)
+ }
+
+ _, err = url.ParseRequestURI(link.URL)
+ if err != nil {
+ return GetDefaultIssueConfig(), fmt.Errorf("%s is not a valid URL", link.URL)
+ }
+ }
+
+ return issueConfig, nil
+}
+
+// IssueConfigFromDefaultBranch returns the issue config for this repo.
+// It never returns a nil config.
+func (ctx *Context) IssueConfigFromDefaultBranch() (api.IssueConfig, error) {
+ if ctx.Repo.Repository.IsEmpty {
+ return GetDefaultIssueConfig(), nil
+ }
+
+ commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
+ if err != nil {
+ return GetDefaultIssueConfig(), err
+ }
+
+ for _, configName := range IssueConfigCandidates {
+ if _, err := commit.GetTreeEntryByPath(configName + ".yaml"); err == nil {
+ return ctx.Repo.GetIssueConfig(configName+".yaml", commit)
+ }
+
+ if _, err := commit.GetTreeEntryByPath(configName + ".yml"); err == nil {
+ return ctx.Repo.GetIssueConfig(configName+".yml", commit)
+ }
+ }
+
+ return GetDefaultIssueConfig(), nil
+}
+
+// IsIssueConfig returns if the given path is a issue config file.
+func (r *Repository) IsIssueConfig(path string) bool {
+ for _, configName := range IssueConfigCandidates {
+ if path == configName+".yaml" || path == configName+".yml" {
+ return true
+ }
+ }
+ return false
+}
+
+func (ctx *Context) HasIssueTemplatesOrContactLinks() bool {
+ if len(ctx.IssueTemplatesFromDefaultBranch()) > 0 {
+ return true
+ }
+
+ issueConfig, _ := ctx.IssueConfigFromDefaultBranch()
+ return len(issueConfig.ContactLinks) > 0
+}
diff --git a/modules/structs/issue.go b/modules/structs/issue.go
index 1d1de9ee5e..04e169df84 100644
--- a/modules/structs/issue.go
+++ b/modules/structs/issue.go
@@ -190,6 +190,22 @@ func (l *IssueTemplateLabels) UnmarshalYAML(value *yaml.Node) error {
return fmt.Errorf("line %d: cannot unmarshal %s into IssueTemplateLabels", value.Line, value.ShortTag())
}
+type IssueConfigContactLink struct {
+ Name string `json:"name" yaml:"name"`
+ URL string `json:"url" yaml:"url"`
+ About string `json:"about" yaml:"about"`
+}
+
+type IssueConfig struct {
+ BlankIssuesEnabled bool `json:"blank_issues_enabled" yaml:"blank_issues_enabled"`
+ ContactLinks []IssueConfigContactLink `json:"contact_links" yaml:"contact_links"`
+}
+
+type IssueConfigValidation struct {
+ Valid bool `json:"valid"`
+ Message string `json:"message"`
+}
+
// IssueTemplateType defines issue template type
type IssueTemplateType string