feat(repo): validate create templates, publish local projects, verify edits

repo create --license NOPE returned Gitea's HTTP 500 internals. License,
.gitignore and label templates are now checked against the server's
lists, case-insensitively, and complete with Tab. --source DIR adds the
new repository as a remote of a local Git repository and --push pushes
its current branch; --public is the explicit opposite of --private, and
-d/-g/-l and the new alias follow gh. --trust-model now offers the names
Gitea accepts. A prompted name is confirmed with the visibility, owner
and host, and creation reports "✓ Created repository ..." and the URL.

repo edit --default-branch nope exited 0 without changing anything; the
branch must now exist. Edit gains --visibility, --enable-issues/-wiki/
-pull-requests/-projects, --allow-merge-commit/-squash/-rebase,
--delete-branch-on-merge, --default-merge-style and --add-topic/
--remove-topic, and reports which settings it changed.

repo migrate names the repository after the source URL, validates
--service before prompting, and reports the import with the URL.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-11 23:51:49 +02:00
1 parent 368de47c40
commit 682c9f66a2
9 files changed
+742 -308

No files matched your search

+31
View File
@@ -72,6 +72,15 @@ type RepositoryEdit struct {
Private *bool `json:"private,omitempty"`
Archived *bool `json:"archived,omitempty"`
Template *bool `json:"template,omitempty"`
Issues *bool `json:"has_issues,omitempty"`
Wiki *bool `json:"has_wiki,omitempty"`
PullRequests *bool `json:"has_pull_requests,omitempty"`
Projects *bool `json:"has_projects,omitempty"`
MergeCommits *bool `json:"allow_merge_commits,omitempty"`
Squash *bool `json:"allow_squash_merge,omitempty"`
Rebase *bool `json:"allow_rebase,omitempty"`
MergeStyle *string `json:"default_merge_style,omitempty"`
DeleteBranch *bool `json:"default_delete_branch_after_merge,omitempty"`
}
// View retrieves one repository.
@@ -169,14 +178,36 @@ func (s Repositories) Create(ctx context.Context, org string, opts RepositoryOpt
// Edit updates only explicitly supplied resource fields.
func (s Repositories) Edit(ctx context.Context, repo string, opts RepositoryEdit) (Repository, error) {
var mergeStyle *gitea.MergeStyle
if opts.MergeStyle != nil {
style := gitea.MergeStyle(*opts.MergeStyle)
mergeStyle = &style
}
return repoConvert(ctx, s.Client, repo, repositoryFrom, func(ctx context.Context, c *gitea.Client, owner, name string) (*gitea.Repository, *gitea.Response, error) {
return c.Repositories.EditRepo(ctx, owner, name, gitea.EditRepoOption{
HasActions: opts.ActionsEnabled, Name: opts.Name, Website: opts.Website, DefaultBranch: opts.DefaultBranch,
Description: opts.Description, Private: opts.Private, Archived: opts.Archived, Template: opts.Template,
HasIssues: opts.Issues, HasWiki: opts.Wiki, HasPullRequests: opts.PullRequests, HasProjects: opts.Projects,
AllowMerge: opts.MergeCommits, AllowSquash: opts.Squash, AllowRebase: opts.Rebase, DefaultMergeStyle: mergeStyle,
DefaultDeleteBranchAfterMerge: opts.DeleteBranch,
})
})
}
// AddTopic adds a topic to the repository.
func (s Repositories) AddTopic(ctx context.Context, repo, topic string) error {
return repoDo(ctx, s.Client, repo, func(ctx context.Context, c *gitea.Client, owner, name string) (*gitea.Response, error) {
return c.Repositories.AddRepoTopic(ctx, owner, name, topic)
})
}
// RemoveTopic removes a topic from the repository.
func (s Repositories) RemoveTopic(ctx context.Context, repo, topic string) error {
return repoDo(ctx, s.Client, repo, func(ctx context.Context, c *gitea.Client, owner, name string) (*gitea.Response, error) {
return c.Repositories.DeleteRepoTopic(ctx, owner, name, topic)
})
}
// Delete removes the selected repository.
func (s Repositories) Delete(ctx context.Context, repo string) error {
return repoDo(ctx, s.Client, repo, func(ctx context.Context, c *gitea.Client, owner, name string) (*gitea.Response, error) {
+24
View File
@@ -45,6 +45,30 @@ func (s Repositories) Generate(ctx context.Context, source string, opts Reposito
return repositoryFrom(&response), nil
}
// Licenses lists the license templates that repository creation accepts.
func (s Repositories) Licenses(ctx context.Context) ([]string, error) {
licenses, _, err := call(ctx, s.Client, func(ctx context.Context, c *gitea.Client) ([]*gitea.LicensesTemplateListEntry, *gitea.Response, error) {
return c.Templates.ListLicenses(ctx)
})
return convertAll(licenses, func(license *gitea.LicensesTemplateListEntry) string { return license.Key }), err
}
// Gitignores lists the .gitignore templates that repository creation accepts.
func (s Repositories) Gitignores(ctx context.Context) ([]string, error) {
names, _, err := call(ctx, s.Client, func(ctx context.Context, c *gitea.Client) ([]string, *gitea.Response, error) {
return c.Templates.ListGitignores(ctx)
})
return names, err
}
// LabelTemplates lists the label sets that repository creation accepts.
func (s Repositories) LabelTemplates(ctx context.Context) ([]string, error) {
names, _, err := call(ctx, s.Client, func(ctx context.Context, c *gitea.Client) ([]string, *gitea.Response, error) {
return c.Templates.ListLabels(ctx)
})
return names, err
}
// RepositoryMigrationOptions is explicit server-side import input. Credentials stay in request bodies.
type RepositoryMigrationOptions struct {
Name string `json:"repo_name"`