Skip to content

Implement Issue Config #20956

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fac2cb6
Allow disabling blank Issues
JakobDev Aug 25, 2022
adb916e
Change supported paths
JakobDev Aug 29, 2022
c7a8ee4
Change config paths
JakobDev Aug 29, 2022
4d008fc
Merge branch 'main' into noblankissues
JakobDev Sep 2, 2022
ecbbbe9
Fix bugs
JakobDev Sep 2, 2022
7b4415b
Return error when getting default branch fails
JakobDev Sep 4, 2022
bdd3c2a
Merge branch 'main' into noblankissues
JakobDev Dec 13, 2022
146bd6b
Add documentation
JakobDev Dec 13, 2022
bb72aea
Refactor code
JakobDev Dec 20, 2022
8624c96
Improve documentation
JakobDev Dec 26, 2022
714e92e
Add API
JakobDev Jan 2, 2023
18fe929
Make error text translatable
JakobDev Jan 2, 2023
099def7
Apply suggestion from wolfogre
JakobDev Jan 9, 2023
fe94b82
Merge branch 'main' into noblankissues
JakobDev Jan 23, 2023
5ae2ed0
Fix linting
JakobDev Jan 23, 2023
0564f5c
Run make generate-swagger
JakobDev Jan 23, 2023
b34913f
Add defer reader.Close()
JakobDev Jan 27, 2023
c1dc3e4
Merge branch 'main' into noblankissues
JakobDev Feb 6, 2023
abf14a5
Implement Contact Links
JakobDev Feb 6, 2023
2a16447
Show choose page when contact links exists
JakobDev Feb 6, 2023
579b8e5
Validate ContactLinks
JakobDev Feb 7, 2023
1f52bb7
Add API to validate issue config
JakobDev Feb 7, 2023
ed09834
Fix lint
JakobDev Feb 7, 2023
0e01deb
Add API tests
JakobDev Feb 7, 2023
b054f49
Merge branch 'main' into noblankissues
JakobDev Feb 7, 2023
17185ab
Changes requested by wolfogre
JakobDev Feb 8, 2023
062375f
Run make fmt
JakobDev Feb 8, 2023
c820b91
Merge branch 'main' into noblankissues
JakobDev Feb 8, 2023
2ad3111
Merge branch 'main' into noblankissues
JakobDev Feb 13, 2023
b9750dc
Merge branch 'main' into noblankissues
JakobDev Feb 14, 2023
7a4c6f8
Merge branch 'main' into noblankissues
JakobDev Mar 27, 2023
159c0f7
Rename IssueConfigValidate to IssueConfigValidation
JakobDev Mar 27, 2023
68f508b
Missed test
JakobDev Mar 27, 2023
609840c
Typo
JakobDev Mar 27, 2023
31d1353
Merge branch 'main' into noblankissues
JakobDev Mar 28, 2023
d4a69d5
Merge branch 'main' into noblankissues
jolheiser Mar 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions docs/content/doc/usage/issue-pull-request-templates.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ Possible file names for issue config:
- `.gitea/ISSUE_TEMPLATE/config.yaml`
- `.gitea/ISSUE_TEMPLATE/config.yml`
- `.gitea/issue_template/config.yaml`
- `.gitea/issue_template/config.yaml`
- `.gitea/issue_template/config.yml`
- `.github/ISSUE_TEMPLATE/config.yaml`
- `.github/ISSUE_TEMPLATE/config.yml`
Expand Down Expand Up @@ -286,10 +285,23 @@ This is a example for a issue config file

```yaml
blank_issues_enabled: true
contact_links:
- name: Gitea
url: https://gitea.io
about: Visit the Gitea Website
```

### Possible Options

| Key | Description | Type | Default |
|----------------------|-------------------------------------------------------------------------------------------------------|---------|---------|
| blank_issues_enabled | If set to false, the User is forced to use a Template | Boolean | true |
| Key | Description | Type | Default |
|----------------------|-------------------------------------------------------------------------------------------------------|--------------------|----------------|
| blank_issues_enabled | If set to false, the User is forced to use a Template | Boolean | true |
| contact_links | Custom Links to show in the Choose Box | Contact Link Array | Empty Array |

### Contact Link

| Key | Description | Type | Required |
|----------------------|-------------------------------------------------------------------------------------------------------|---------|----------|
| name | the name of your link | String | true |
| url | The URL of your Link | String | true |
| about | A short description of your Link | String | true |
39 changes: 36 additions & 3 deletions modules/context/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ var IssueTemplateDirCandidates = []string{
".gitlab/issue_template",
}

var IssueConfigCanidates = []string{
var IssueConfigCandidates = []string{
".gitea/ISSUE_TEMPLATE/config",
".gitea/issue_template/config",
".github/ISSUE_TEMPLATE/config",
Expand Down Expand Up @@ -1112,6 +1112,7 @@ func (ctx *Context) IssueTemplatesErrorsFromDefaultBranch() ([]*api.IssueTemplat
func GetDefaultIssueConfig() api.IssueConfig {
return api.IssueConfig{
BlankIssuesEnabled: true,
ContactLinks: make([]api.IssueConfigContactLink, 0),
}
}

Expand Down Expand Up @@ -1147,18 +1148,41 @@ func (r *Repository) GetIssueConfig(path string, commit *git.Commit) (api.IssueC
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 IssueConfigCanidates {
for _, configName := range IssueConfigCandidates {
if _, err := commit.GetTreeEntryByPath(configName + ".yaml"); err == nil {
return ctx.Repo.GetIssueConfig(configName+".yaml", commit)
}
Expand All @@ -1173,10 +1197,19 @@ func (ctx *Context) IssueConfigFromDefaultBranch() (api.IssueConfig, error) {

// IsIssueConfig returns if the given path is a issue config file.
func (r *Repository) IsIssueConfig(path string) bool {
for _, configName := range IssueConfigCanidates {
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
}
14 changes: 13 additions & 1 deletion modules/structs/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,20 @@ 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"`
BlankIssuesEnabled bool `json:"blank_issues_enabled" yaml:"blank_issues_enabled"`
ContactLinks []IssueConfigContactLink `json:"contact_links" yaml:"contact_links"`
}

type IssueConfigValidate struct {
Valid bool `json:"valid"`
Message string `json:"message"`
}

// IssueTemplateType defines issue template type
Expand Down
3 changes: 2 additions & 1 deletion options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1267,11 +1267,12 @@ issues.new.no_assignees = No Assignees
issues.new.no_reviewers = No reviewers
issues.new.add_reviewer_title = Request review
issues.choose.get_started = Get Started
issues.choose.open_external_link = Open
issues.choose.blank = Default
issues.choose.blank_about = Create an issue from default template.
issues.choose.ignore_invalid_templates = Invalid templates have been ignored
issues.choose.invalid_templates = %v invalid template(s) found
issues.choose.invalid_config = Your issue_config.yaml contains a error:
issues.choose.invalid_config = The issue config contains errors:
issues.no_ref = No Branch/Tag Specified
issues.create = Create Issue
issues.new_label = New Label
Expand Down
1 change: 1 addition & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,7 @@ func Routes(ctx gocontext.Context) *web.Route {
}, reqAnyRepoReader())
m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates)
m.Get("/issue_config", context.ReferencesGitRepo(), repo.GetIssueConfig)
m.Get("/issue_config/validate", context.ReferencesGitRepo(), repo.ValidateIssueConfig)
m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages)
}, repoAssignment())
})
Expand Down
43 changes: 42 additions & 1 deletion routers/api/v1/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1107,9 +1107,50 @@ func GetIssueConfig(ctx *context.APIContext) {
// summary: Returns the issue config for a repo
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/IssueConfig"
// "$ref": "#/responses/RepoIssueConfig"
issueConfig, _ := ctx.IssueConfigFromDefaultBranch()
ctx.JSON(http.StatusOK, issueConfig)
}

// ValidateIssueConfig returns validation errors for the issue config
func ValidateIssueConfig(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/issue_config/validate repository repoValidateIssueConfig
// ---
// summary: Returns the validation information for a issue config
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/RepoIssueConfigValidate"
_, err := ctx.IssueConfigFromDefaultBranch()

if err == nil {
ctx.JSON(http.StatusOK, api.IssueConfigValidate{Valid: true, Message: ""})
} else {
ctx.JSON(http.StatusOK, api.IssueConfigValidate{Valid: false, Message: err.Error()})
}
}
14 changes: 14 additions & 0 deletions routers/api/v1/swagger/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,17 @@ type swaggerRepoCollaboratorPermission struct {
// in:body
Body api.RepoCollaboratorPermission `json:"body"`
}

// RepoIssueConfig
// swagger:response RepoIssueConfig
type swaggerRepoIssueConfig struct {
// in:body
Body api.IssueConfig `json:"body"`
}

// RepoIssueConfigValidate
// swagger:response RepoIssueConfigValidate
type swaggerRepoIssueConfigValidate struct {
// in:body
Body api.IssueConfigValidate `json:"body"`
}
10 changes: 5 additions & 5 deletions routers/web/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ func Issues(ctx *context.Context) {
}
ctx.Data["Title"] = ctx.Tr("repo.issues")
ctx.Data["PageIsIssueList"] = true
ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
ctx.Data["NewIssueChooseTemplate"] = ctx.HasIssueTemplatesOrContactLinks()
}

issues(ctx, ctx.FormInt64("milestone"), ctx.FormInt64("project"), util.OptionalBoolOf(isPullList))
Expand Down Expand Up @@ -827,7 +827,7 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles
func NewIssue(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true
ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
ctx.Data["NewIssueChooseTemplate"] = ctx.HasIssueTemplatesOrContactLinks()
ctx.Data["RequireTribute"] = true
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
title := ctx.FormString("title")
Expand Down Expand Up @@ -925,7 +925,7 @@ func NewIssueChooseTemplate(ctx *context.Context) {
ctx.Flash.Warning(renderErrorOfTemplates(ctx, errs), true)
}

if len(issueTemplates) == 0 {
if !ctx.HasIssueTemplatesOrContactLinks() {
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if no template here, just redirect to the "issues/new" page with these parameters.
ctx.Redirect(fmt.Sprintf("%s/issues/new?%s", ctx.Repo.Repository.Link(), ctx.Req.URL.RawQuery), http.StatusSeeOther)
return
Expand Down Expand Up @@ -1069,7 +1069,7 @@ func NewIssuePost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CreateIssueForm)
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true
ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
ctx.Data["NewIssueChooseTemplate"] = ctx.HasIssueTemplatesOrContactLinks()
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
Expand Down Expand Up @@ -1259,7 +1259,7 @@ func ViewIssue(ctx *context.Context) {
return
}
ctx.Data["PageIsIssueList"] = true
ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
ctx.Data["NewIssueChooseTemplate"] = ctx.HasIssueTemplatesOrContactLinks()
}

if issue.IsPull && !ctx.Repo.CanRead(unit.TypeIssues) {
Expand Down
13 changes: 13 additions & 0 deletions templates/repo/issue/choose.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@
</div>
</div>
{{end}}
{{range .IssueConfig.ContactLinks}}
<div class="ui attached segment">
<div class="ui two column grid">
<div class="column left aligned">
<strong>{{.Name | RenderEmojiPlain}}</strong>
<br/>{{.About | RenderEmojiPlain}}
</div>
<div class="column right aligned">
<a href="{{.URL}}" class="ui green button">{{svg "octicon-link-external"}} {{$.locale.Tr "repo.issues.choose.open_external_link"}}</a>
</div>
</div>
</div>
{{end}}
{{if .IssueConfig.BlankIssuesEnabled}}
<div class="ui attached segment">
<div class="ui two column grid">
Expand Down
Loading