diff options
author | JakobDev <jakobdev@gmx.de> | 2023-03-28 20:22:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-28 14:22:07 -0400 |
commit | f384b13f1cd44be3a87df5553a0099390dacd010 (patch) | |
tree | 08b2744df3a8792ef2f50e4d559d55e4a349c198 /modules | |
parent | 5cd1d6c93ba9b8399f826e671b8940eb5294b872 (diff) | |
download | gitea-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.go | 114 | ||||
-rw-r--r-- | modules/structs/issue.go | 16 |
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 |