Add option to initialize repository with labels (#6061)
* Add optional label sets on repo creation * Fix CRLF * Instead of hardcoding default, make it the helper * Move label set init out of repo init Add a new error for the router Combine router label init with repo creation label init Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add issue labels to Swagger for repo creation Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update models/issue_label.go Co-Authored-By: Lauris BH <lauris@nix.lv> * Update models/issue_label.go Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									d4e11ebb18
								
							
						
					
					
						commit
						0118b6aaf8
					
				
					 11 changed files with 87 additions and 16 deletions
				
			
		|  | @ -1058,6 +1058,22 @@ func (err ErrIssueNotExist) Error() string { | ||||||
| 	return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) | 	return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ErrIssueLabelTemplateLoad represents a "ErrIssueLabelTemplateLoad" kind of error.
 | ||||||
|  | type ErrIssueLabelTemplateLoad struct { | ||||||
|  | 	TemplateFile  string | ||||||
|  | 	OriginalError error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrIssueLabelTemplateLoad checks if an error is a ErrIssueLabelTemplateLoad.
 | ||||||
|  | func IsErrIssueLabelTemplateLoad(err error) bool { | ||||||
|  | 	_, ok := err.(ErrIssueLabelTemplateLoad) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrIssueLabelTemplateLoad) Error() string { | ||||||
|  | 	return fmt.Sprintf("Failed to load label template file '%s': %v", err.TemplateFile, err.OriginalError) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // __________      .__  .__ __________                                     __
 | // __________      .__  .__ __________                                     __
 | ||||||
| // \______   \__ __|  | |  |\______   \ ____  ________ __   ____   _______/  |_
 | // \______   \__ __|  | |  |\______   \ ____  ________ __   ____   _______/  |_
 | ||||||
| //  |     ___/  |  \  | |  | |       _// __ \/ ____/  |  \_/ __ \ /  ___/\   __\
 | //  |     ___/  |  \  | |  | |       _// __ \/ ____/  |  \_/ __ \ /  ___/\   __\
 | ||||||
|  |  | ||||||
|  | @ -127,6 +127,34 @@ func (label *Label) ForegroundColor() template.CSS { | ||||||
| 	return template.CSS("#000") | 	return template.CSS("#000") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func initalizeLabels(e Engine, repoID int64, labelTemplate string) error { | ||||||
|  | 	list, err := GetLabelTemplateFile(labelTemplate) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return ErrIssueLabelTemplateLoad{labelTemplate, err} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	labels := make([]*Label, len(list)) | ||||||
|  | 	for i := 0; i < len(list); i++ { | ||||||
|  | 		labels[i] = &Label{ | ||||||
|  | 			RepoID:      repoID, | ||||||
|  | 			Name:        list[i][0], | ||||||
|  | 			Description: list[i][2], | ||||||
|  | 			Color:       list[i][1], | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for _, label := range labels { | ||||||
|  | 		if err = newLabel(e, label); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // InitalizeLabels adds a label set to a repository using a template
 | ||||||
|  | func InitalizeLabels(repoID int64, labelTemplate string) error { | ||||||
|  | 	return initalizeLabels(x, repoID, labelTemplate) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func newLabel(e Engine, label *Label) error { | func newLabel(e Engine, label *Label) error { | ||||||
| 	_, err := e.Insert(label) | 	_, err := e.Insert(label) | ||||||
| 	return err | 	return err | ||||||
|  |  | ||||||
|  | @ -1098,6 +1098,7 @@ type CreateRepoOptions struct { | ||||||
| 	Description string | 	Description string | ||||||
| 	OriginalURL string | 	OriginalURL string | ||||||
| 	Gitignores  string | 	Gitignores  string | ||||||
|  | 	IssueLabels string | ||||||
| 	License     string | 	License     string | ||||||
| 	Readme      string | 	Readme      string | ||||||
| 	IsPrivate   bool | 	IsPrivate   bool | ||||||
|  | @ -1394,6 +1395,13 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err | ||||||
| 			return nil, fmt.Errorf("initRepository: %v", err) | 			return nil, fmt.Errorf("initRepository: %v", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		// Initialize Issue Labels if selected
 | ||||||
|  | 		if len(opts.IssueLabels) > 0 { | ||||||
|  | 			if err = initalizeLabels(sess, repo.ID, opts.IssueLabels); err != nil { | ||||||
|  | 				return nil, fmt.Errorf("initalizeLabels: %v", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		_, stderr, err := process.GetManager().ExecDir(-1, | 		_, stderr, err := process.GetManager().ExecDir(-1, | ||||||
| 			repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), | 			repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), | ||||||
| 			git.GitExecutable, "update-server-info") | 			git.GitExecutable, "update-server-info") | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ type CreateRepoForm struct { | ||||||
| 	Description string `binding:"MaxSize(255)"` | 	Description string `binding:"MaxSize(255)"` | ||||||
| 	AutoInit    bool | 	AutoInit    bool | ||||||
| 	Gitignores  string | 	Gitignores  string | ||||||
|  | 	IssueLabels string | ||||||
| 	License     string | 	License     string | ||||||
| 	Readme      string | 	Readme      string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -67,6 +67,8 @@ type CreateRepoOption struct { | ||||||
| 	Description string `json:"description" binding:"MaxSize(255)"` | 	Description string `json:"description" binding:"MaxSize(255)"` | ||||||
| 	// Whether the repository is private
 | 	// Whether the repository is private
 | ||||||
| 	Private bool `json:"private"` | 	Private bool `json:"private"` | ||||||
|  | 	// Issue Label set to use
 | ||||||
|  | 	IssueLabels string `json:"issue_labels"` | ||||||
| 	// Whether the repository should be auto-intialized?
 | 	// Whether the repository should be auto-intialized?
 | ||||||
| 	AutoInit bool `json:"auto_init"` | 	AutoInit bool `json:"auto_init"` | ||||||
| 	// Gitignores to use
 | 	// Gitignores to use
 | ||||||
|  |  | ||||||
|  | @ -578,6 +578,8 @@ fork_visibility_helper = The visibility of a forked repository cannot be changed | ||||||
| repo_desc = Description | repo_desc = Description | ||||||
| repo_lang = Language | repo_lang = Language | ||||||
| repo_gitignore_helper = Select .gitignore templates. | repo_gitignore_helper = Select .gitignore templates. | ||||||
|  | issue_labels = Issue Labels | ||||||
|  | issue_labels_helper = Select an issue label set. | ||||||
| license = License | license = License | ||||||
| license_helper = Select a license file. | license_helper = Select a license file. | ||||||
| readme = README | readme = README | ||||||
|  |  | ||||||
|  | @ -206,6 +206,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR | ||||||
| 	repo, err := models.CreateRepository(ctx.User, owner, models.CreateRepoOptions{ | 	repo, err := models.CreateRepository(ctx.User, owner, models.CreateRepoOptions{ | ||||||
| 		Name:        opt.Name, | 		Name:        opt.Name, | ||||||
| 		Description: opt.Description, | 		Description: opt.Description, | ||||||
|  | 		IssueLabels: opt.IssueLabels, | ||||||
| 		Gitignores:  opt.Gitignores, | 		Gitignores:  opt.Gitignores, | ||||||
| 		License:     opt.License, | 		License:     opt.License, | ||||||
| 		Readme:      opt.Readme, | 		Readme:      opt.Readme, | ||||||
|  |  | ||||||
|  | @ -33,24 +33,15 @@ func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { | ||||||
| 		ctx.Redirect(ctx.Repo.RepoLink + "/labels") | 		ctx.Redirect(ctx.Repo.RepoLink + "/labels") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	list, err := models.GetLabelTemplateFile(form.TemplateName) |  | ||||||
| 	if err != nil { |  | ||||||
| 		ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, err)) |  | ||||||
| 		ctx.Redirect(ctx.Repo.RepoLink + "/labels") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	labels := make([]*models.Label, len(list)) | 	if err := models.InitalizeLabels(ctx.Repo.Repository.ID, form.TemplateName); err != nil { | ||||||
| 	for i := 0; i < len(list); i++ { | 		if models.IsErrIssueLabelTemplateLoad(err) { | ||||||
| 		labels[i] = &models.Label{ | 			originalErr := err.(models.ErrIssueLabelTemplateLoad).OriginalError | ||||||
| 			RepoID:      ctx.Repo.Repository.ID, | 			ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, originalErr)) | ||||||
| 			Name:        list[i][0], | 			ctx.Redirect(ctx.Repo.RepoLink + "/labels") | ||||||
| 			Description: list[i][2], | 			return | ||||||
| 			Color:       list[i][1], |  | ||||||
| 		} | 		} | ||||||
| 	} | 		ctx.ServerError("InitalizeLabels", err) | ||||||
| 	if err := models.NewLabels(labels...); err != nil { |  | ||||||
| 		ctx.ServerError("NewLabels", err) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	ctx.Redirect(ctx.Repo.RepoLink + "/labels") | 	ctx.Redirect(ctx.Repo.RepoLink + "/labels") | ||||||
|  |  | ||||||
|  | @ -115,6 +115,7 @@ func Create(ctx *context.Context) { | ||||||
| 
 | 
 | ||||||
| 	// Give default value for template to render.
 | 	// Give default value for template to render.
 | ||||||
| 	ctx.Data["Gitignores"] = models.Gitignores | 	ctx.Data["Gitignores"] = models.Gitignores | ||||||
|  | 	ctx.Data["LabelTemplates"] = models.LabelTemplates | ||||||
| 	ctx.Data["Licenses"] = models.Licenses | 	ctx.Data["Licenses"] = models.Licenses | ||||||
| 	ctx.Data["Readmes"] = models.Readmes | 	ctx.Data["Readmes"] = models.Readmes | ||||||
| 	ctx.Data["readme"] = "Default" | 	ctx.Data["readme"] = "Default" | ||||||
|  | @ -155,6 +156,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { | ||||||
| 	ctx.Data["Title"] = ctx.Tr("new_repo") | 	ctx.Data["Title"] = ctx.Tr("new_repo") | ||||||
| 
 | 
 | ||||||
| 	ctx.Data["Gitignores"] = models.Gitignores | 	ctx.Data["Gitignores"] = models.Gitignores | ||||||
|  | 	ctx.Data["LabelTemplates"] = models.LabelTemplates | ||||||
| 	ctx.Data["Licenses"] = models.Licenses | 	ctx.Data["Licenses"] = models.Licenses | ||||||
| 	ctx.Data["Readmes"] = models.Readmes | 	ctx.Data["Readmes"] = models.Readmes | ||||||
| 
 | 
 | ||||||
|  | @ -173,6 +175,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { | ||||||
| 		Name:        form.RepoName, | 		Name:        form.RepoName, | ||||||
| 		Description: form.Description, | 		Description: form.Description, | ||||||
| 		Gitignores:  form.Gitignores, | 		Gitignores:  form.Gitignores, | ||||||
|  | 		IssueLabels: form.IssueLabels, | ||||||
| 		License:     form.License, | 		License:     form.License, | ||||||
| 		Readme:      form.Readme, | 		Readme:      form.Readme, | ||||||
| 		IsPrivate:   form.Private || setting.Repository.ForcePrivate, | 		IsPrivate:   form.Private || setting.Repository.ForcePrivate, | ||||||
|  |  | ||||||
|  | @ -56,6 +56,20 @@ | ||||||
| 						<textarea id="description" name="description">{{.description}}</textarea> | 						<textarea id="description" name="description">{{.description}}</textarea> | ||||||
| 					</div> | 					</div> | ||||||
| 
 | 
 | ||||||
|  | 					<div class="inline field"> | ||||||
|  | 						<label>{{.i18n.Tr "repo.issue_labels"}}</label> | ||||||
|  | 						<div class="ui search normal selection dropdown"> | ||||||
|  | 							<input type="hidden" name="issue_labels" value="{{.issueLabels}}"> | ||||||
|  | 							<div class="default text">{{.i18n.Tr "repo.issue_labels_helper"}}</div> | ||||||
|  | 							<div class="menu"> | ||||||
|  | 								<div class="item" data-value="">{{.i18n.Tr "repo.issue_labels_helper"}}</div> | ||||||
|  |                                 {{range .LabelTemplates}} | ||||||
|  | 									<div class="item" data-value="{{.}}">{{.}}</div> | ||||||
|  |                                 {{end}} | ||||||
|  | 							</div> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 
 | ||||||
| 					<div class="ui divider"></div> | 					<div class="ui divider"></div> | ||||||
| 
 | 
 | ||||||
| 					<div class="inline field"> | 					<div class="inline field"> | ||||||
|  |  | ||||||
|  | @ -7843,6 +7843,11 @@ | ||||||
|           "type": "string", |           "type": "string", | ||||||
|           "x-go-name": "Gitignores" |           "x-go-name": "Gitignores" | ||||||
|         }, |         }, | ||||||
|  |         "issue_labels": { | ||||||
|  |           "description": "Issue Label set to use", | ||||||
|  |           "type": "string", | ||||||
|  |           "x-go-name": "IssueLabels" | ||||||
|  |         }, | ||||||
|         "license": { |         "license": { | ||||||
|           "description": "License to use", |           "description": "License to use", | ||||||
|           "type": "string", |           "type": "string", | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue