refactor(repo,org,team,release): trim help texts, obvious comments and single-use helpers

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-12 20:48:01 +02:00
1 parent d5aef95855
commit b79845b9f3
11 files changed
+55 -129

No files matched your search

+3 -9
View File
@@ -21,11 +21,7 @@ func (a *application) adminCommand() *cobra.Command {
group := &cobra.Command{Use: "admin", Short: "Manage instance administration", Long: "Administer the Gitea instance. These commands need an administrator account."} group := &cobra.Command{Use: "admin", Short: "Manage instance administration", Long: "Administer the Gitea instance. These commands need an administrator account."}
users := &cobra.Command{Use: "user", Short: "Manage registered accounts", Long: `List, view, create, edit and delete the accounts of the instance. These commands need an users := &cobra.Command{Use: "user", Short: "Manage registered accounts", Long: `List, view, create, edit and delete the accounts of the instance. These commands need an
administrator account.`} administrator account.`}
users.AddCommand(a.adminUserListCommand(), a.adminUserViewCommand()) users.AddCommand(a.adminUserListCommand(), a.adminUserViewCommand(), a.adminUserChangeCommand("create"), a.adminUserChangeCommand("edit"), a.adminUserDeleteCommand())
for _, verb := range []string{"create", "edit"} {
users.AddCommand(a.adminUserChangeCommand(verb))
}
users.AddCommand(a.adminUserDeleteCommand())
group.AddCommand(users) group.AddCommand(users)
return group return group
} }
@@ -149,7 +145,6 @@ $ tea admin user view bob --json email,admin`,
} }
} }
// adminUserChangeCommand creates or edits an account.
func (a *application) adminUserChangeCommand(verb string) *cobra.Command { func (a *application) adminUserChangeCommand(verb string) *cobra.Command {
var username, email, fullName, description, website, location, visibility, password, passwordFile string var username, email, fullName, description, website, location, visibility, password, passwordFile string
var admin, active, restricted, prohibitLogin, mustChangePassword, allowGitHook, allowImportLocal, allowCreateOrganization bool var admin, active, restricted, prohibitLogin, mustChangePassword, allowGitHook, allowImportLocal, allowCreateOrganization bool
@@ -330,9 +325,8 @@ func (a *application) adminUserDeleteCommand() *cobra.Command {
var purge bool var purge bool
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "delete <username>", Short: "Delete an account", Args: argsExact(1), ValidArgsFunction: a.completeAdminUsers(), Use: "delete <username>", Short: "Delete an account", Args: argsExact(1), ValidArgsFunction: a.completeAdminUsers(),
Long: `Delete an account. Gitea refuses while the user owns repositories or is the last owner of an Long: `Delete an account. Gitea refuses while the user owns repositories or organizations, unless
organization: transfer or delete those first, or pass --purge to delete the account with --purge deletes the account with everything it owns.`,
everything it owns. A purge cannot be undone, so a terminal asks you to type the username.`,
Example: `$ tea admin user delete spammer Example: `$ tea admin user delete spammer
# also delete the user's repositories, packages and organization memberships # also delete the user's repositories, packages and organization memberships
$ tea admin user delete spammer --purge`, $ tea admin user delete spammer --purge`,
+11 -26
View File
@@ -18,23 +18,17 @@ import (
// validateBranch rejects invalid Git refs before making requests. // validateBranch rejects invalid Git refs before making requests.
func validateBranch(name string) error { func validateBranch(name string) error {
if name == "" || name == "@" || strings.HasPrefix(name, "-") || strings.HasPrefix(name, "/") || strings.HasSuffix(name, "/") || strings.HasSuffix(name, ".") || strings.Contains(name, "..") || strings.Contains(name, "@{") || strings.Contains(name, "//") || strings.ContainsAny(name, " ~^:?*[\\\t\r\n") { invalid := name == "" || name == "@" || strings.HasPrefix(name, "-") || strings.HasPrefix(name, "/") || strings.HasSuffix(name, "/") || strings.HasSuffix(name, ".") || strings.Contains(name, "..") || strings.Contains(name, "@{") || strings.Contains(name, "//") || strings.ContainsAny(name, " ~^:?*[\\\t\r\n") ||
return clierr.Wrap(clierr.Usage, errors.New("invalid branch name")) strings.ContainsFunc(name, func(r rune) bool { return r < 32 || r == 127 })
}
for part := range strings.SplitSeq(name, "/") { for part := range strings.SplitSeq(name, "/") {
if strings.HasPrefix(part, ".") || strings.HasSuffix(part, ".lock") { invalid = invalid || strings.HasPrefix(part, ".") || strings.HasSuffix(part, ".lock")
}
if invalid {
return clierr.Wrap(clierr.Usage, errors.New("invalid branch name")) return clierr.Wrap(clierr.Usage, errors.New("invalid branch name"))
} }
}
for _, r := range name {
if r < 32 || r == 127 {
return clierr.Wrap(clierr.Usage, errors.New("invalid branch name"))
}
}
return nil return nil
} }
// validateBranches validates every branch name.
func validateBranches(names []string) error { func validateBranches(names []string) error {
for _, name := range names { for _, name := range names {
if err := validateBranch(name); err != nil { if err := validateBranch(name); err != nil {
@@ -249,7 +243,10 @@ $ tea branch delete old-a old-b --yes`,
if err != nil { if err != nil {
return err return err
} }
subject := branchSubject(args) subject := "branch " + args[0]
if len(args) > 1 {
subject = fmt.Sprintf("%d branches (%s)", len(args), strings.Join(args, ", "))
}
if err := a.confirmAction(cmd, terminal.Confirmation{Prompt: "Delete " + subject + " from " + target.Repo + "?"}); err != nil { if err := a.confirmAction(cmd, terminal.Confirmation{Prompt: "Delete " + subject + " from " + target.Repo + "?"}); err != nil {
return err return err
} }
@@ -263,14 +260,6 @@ $ tea branch delete old-a old-b --yes`,
return command return command
} }
// branchSubject names one or more branches in a prompt: "branch main" or "2 branches (a, b)".
func branchSubject(names []string) string {
if len(names) == 1 {
return "branch " + names[0]
}
return fmt.Sprintf("%d branches (%s)", len(names), strings.Join(names, ", "))
}
func (a *application) branchProtectCommand() *cobra.Command { func (a *application) branchProtectCommand() *cobra.Command {
var rule services.Protection var rule services.Protection
var pattern string var pattern string
@@ -278,12 +267,8 @@ func (a *application) branchProtectCommand() *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
Use: "protect <branch>...", Use: "protect <branch>...",
Short: "Protect branches", Short: "Protect branches",
Long: `Add a protection rule for each branch. By default nobody can push to a protected branch directly Long: `Add a protection rule for each existing branch, or with --pattern for every branch matching a
and pull requests need one approving review to merge; --allow-push lets users or teams push, and glob. By default nobody can push directly and pull requests need one approving review.`,
--required-approvals changes the review count.
The branches must exist. --pattern protects every branch that matches a glob instead, such as
release/*, including branches created later.`,
Example: `# Require two approvals and passing CI on main Example: `# Require two approvals and passing CI on main
$ tea branch protect main --required-approvals 2 --status-check ci/build $ tea branch protect main --required-approvals 2 --status-check ci/build
+7 -19
View File
@@ -19,7 +19,6 @@ import (
var visibilities = []string{"public", "limited", "private"} var visibilities = []string{"public", "limited", "private"}
// visibilityColors color the visibility of organizations and accounts.
var visibilityColors = map[string]string{"public": "green", "limited": "yellow", "private": "red"} var visibilityColors = map[string]string{"public": "green", "limited": "yellow", "private": "red"}
// timeField shows an API timestamp relative to now on a TTY, and as RFC3339 for scripts. // timeField shows an API timestamp relative to now on a TTY, and as RFC3339 for scripts.
@@ -38,14 +37,6 @@ func timeField(label, raw string, d terminal.Display) terminal.Field {
return terminal.Field{Label: label, Value: terminal.RelativeTime(now, t)} return terminal.Field{Label: label, Value: terminal.RelativeTime(now, t)}
} }
// countOf words a count, such as "1 member" or "2 members".
func countOf(n int, singular, plural string) string {
if n == 1 {
return "1 " + singular
}
return strconv.Itoa(n) + " " + plural
}
func (a *application) organizationCommand() *cobra.Command { func (a *application) organizationCommand() *cobra.Command {
group := &cobra.Command{Use: "org", Short: "Manage organizations", Long: `Work with Gitea organizations: list the ones you belong to, view, create, edit and delete group := &cobra.Command{Use: "org", Short: "Manage organizations", Long: `Work with Gitea organizations: list the ones you belong to, view, create, edit and delete
them, and manage their members. them, and manage their members.
@@ -104,7 +95,7 @@ $ tea org list --all`,
} }
func (a *application) organizationViewCommand() *cobra.Command { func (a *application) organizationViewCommand() *cobra.Command {
cmd := &cobra.Command{ return &cobra.Command{
Use: "view <org>", Short: "View an organization", Args: argsExact(1), Use: "view <org>", Short: "View an organization", Args: argsExact(1),
Example: `$ tea org view acme Example: `$ tea org view acme
# print the description only # print the description only
@@ -131,7 +122,6 @@ $ tea org view acme --jq .description`,
}) })
}, },
} }
return cmd
} }
func organizationView(org services.Organization, size services.OrganizationSize, address string) terminal.View { func organizationView(org services.Organization, size services.OrganizationSize, address string) terminal.View {
@@ -149,7 +139,11 @@ func organizationView(org services.Organization, size services.OrganizationSize,
singular, plural string singular, plural string
}{{"Members", size.Members, "member", "members"}, {"Teams", size.Teams, "team", "teams"}, {"Repositories", size.Repositories, "repository", "repositories"}} { }{{"Members", size.Members, "member", "members"}, {"Teams", size.Teams, "team", "teams"}, {"Repositories", size.Repositories, "repository", "repositories"}} {
if count.n != nil { if count.n != nil {
counts = append(counts, countOf(*count.n, count.singular, count.plural)) noun := count.plural
if *count.n == 1 {
noun = count.singular
}
counts = append(counts, strconv.Itoa(*count.n)+" "+noun)
view.Fields = append(view.Fields, terminal.Field{Label: count.label, Value: strconv.Itoa(*count.n), PipedOnly: true}) view.Fields = append(view.Fields, terminal.Field{Label: count.label, Value: strconv.Itoa(*count.n), PipedOnly: true})
} }
} }
@@ -200,7 +194,6 @@ func organizationFlags(cmd *cobra.Command, create bool) func() services.Organiza
} }
} }
// organizationName rejects an empty organization argument.
func organizationName(raw string) (string, error) { func organizationName(raw string) (string, error) {
if name := strings.TrimSpace(raw); name != "" { if name := strings.TrimSpace(raw); name != "" {
return name, nil return name, nil
@@ -270,11 +263,7 @@ $ tea org edit acme --visibility private`,
func (a *application) organizationDeleteCommand() *cobra.Command { func (a *application) organizationDeleteCommand() *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "delete <org>", Short: "Delete an organization", Args: argsExact(1), ValidArgsFunction: a.completeOrgs(), Use: "delete <org>", Short: "Delete an organization", Args: argsExact(1), ValidArgsFunction: a.completeOrgs(),
Long: `Delete an organization with its teams. Gitea refuses while the organization owns Long: "Delete an organization with its teams. Gitea refuses while the organization owns repositories.",
repositories: transfer or delete them first.
The deletion cannot be undone, so a terminal asks you to type the organization's name; --yes
skips the question.`,
Example: `$ tea org delete acme-old Example: `$ tea org delete acme-old
$ tea org delete acme-old --yes`, $ tea org delete acme-old --yes`,
Annotations: map[string]string{"tea.text": "organization deletion does not return a resource", "tea.confirm": "true"}, Annotations: map[string]string{"tea.text": "organization deletion does not return a resource", "tea.confirm": "true"},
@@ -371,7 +360,6 @@ func (a *application) completeOrganizationMembers() cobra.CompletionFunc {
} }
} }
// organizationVisibilityCommand publicizes or conceals an organization membership.
func (a *application) organizationVisibilityCommand(public bool) *cobra.Command { func (a *application) organizationVisibilityCommand(public bool) *cobra.Command {
verb, alias, short, state := "conceal", "private", "Hide a membership", "concealed" verb, alias, short, state := "conceal", "private", "Hide a membership", "concealed"
if public { if public {
+3 -7
View File
@@ -165,11 +165,9 @@ func (a *application) releaseDownloadCommand() *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
Use: "download [<tag>] [<asset>...]", Use: "download [<tag>] [<asset>...]",
Short: "Download release assets", Short: "Download release assets",
Long: `Download assets of a release, by name or ID, or those matching --pattern; without either, every Long: `Download the assets of a release (default: the latest) by name or ID, those matching --pattern,
asset. --archive downloads the source code archive instead. Without a tag, the latest release is used. or all of them; --archive downloads the source code instead. Existing files are replaced only with
--clobber, and --output - writes one asset to standard output.`,
Files are written to the current directory under their names, to --dir, or to --output; an existing
file is replaced only with --clobber. --output - writes one asset to standard output.`,
Example: `# Download every asset of the latest release Example: `# Download every asset of the latest release
$ tea release download $ tea release download
@@ -285,7 +283,6 @@ func selectAssets(release services.Release, names, patterns []string) ([]service
return selected, nil return selected, nil
} }
// downloadArchive downloads the source code of the tag as a zip or tar.gz file.
func (a *application) downloadArchive(cmd *cobra.Command, service services.Releases, flags downloadFlags, tag, format string) error { func (a *application) downloadArchive(cmd *cobra.Command, service services.Releases, flags downloadFlags, tag, format string) error {
_, repo, _ := strings.Cut(service.Repository, "/") _, repo, _ := strings.Cut(service.Repository, "/")
name := repo + "-" + strings.ReplaceAll(tag, "/", "-") + "." + format name := repo + "-" + strings.ReplaceAll(tag, "/", "-") + "." + format
@@ -311,7 +308,6 @@ func (a *application) downloadArchive(cmd *cobra.Command, service services.Relea
return nil return nil
} }
// countingReader counts the bytes read through it.
type countingReader struct { type countingReader struct {
reader io.Reader reader io.Reader
n int64 n int64
-2
View File
@@ -139,7 +139,6 @@ $ tea release view v1.0.0 --web`,
return command return command
} }
// releaseView shows a release with its notes as the body.
func (a *application) releaseView(item services.Release, latest int64) terminal.View { func (a *application) releaseView(item services.Release, latest int64) terminal.View {
view := terminal.View{Title: cmp.Or(item.Title, item.Tag), Body: item.Body, Kind: "release", URL: item.URL} view := terminal.View{Title: cmp.Or(item.Title, item.Tag), Body: item.Body, Kind: "release", URL: item.URL}
if item.Title != "" && item.Title != item.Tag { if item.Title != "" && item.Title != item.Tag {
@@ -187,7 +186,6 @@ func (f *releaseFields) register(a *application, cmd *cobra.Command) {
_ = cmd.RegisterFlagCompletionFunc("target", a.completeBranches()) _ = cmd.RegisterFlagCompletionFunc("target", a.completeBranches())
} }
// options returns the release fields whose flags were passed.
func (f *releaseFields) options(a *application, cmd *cobra.Command) (services.ReleaseOptions, error) { func (f *releaseFields) options(a *application, cmd *cobra.Command) (services.ReleaseOptions, error) {
opts := services.ReleaseOptions{} opts := services.ReleaseOptions{}
changed := cmd.Flags().Changed changed := cmd.Flags().Changed
+13 -27
View File
@@ -166,14 +166,12 @@ $ tea repo edit --default-merge-style squash --add-topic cli --add-topic go`,
return services.Classify(err, "branch", strconv.Quote(defaultBranch), t.Repo) return services.Classify(err, "branch", strconv.Quote(defaultBranch), t.Repo)
} }
} }
if options.Archived != nil && *options.Archived {
if err := a.confirmAction(cmd, terminal.Confirmation{Prompt: "Archive " + t.Repo + "? It becomes read-only."}); err != nil {
return err
}
}
// Gitea refuses topic changes on an archived repository, so archiving comes last. // Gitea refuses topic changes on an archived repository, so archiving comes last.
repo, archive := t.Repo, options.Archived != nil && *options.Archived repo, archive := t.Repo, options.Archived != nil && *options.Archived
if archive { if archive {
if err := a.confirmAction(cmd, terminal.Confirmation{Prompt: "Archive " + t.Repo + "? It becomes read-only."}); err != nil {
return err
}
options.Archived = nil options.Archived = nil
} }
if options != (services.RepositoryEdit{}) { if options != (services.RepositoryEdit{}) {
@@ -241,12 +239,9 @@ func (a *application) repoCloneCommand() *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
Use: "clone <repository> [<directory>] [-- <gitflags>...]", Use: "clone <repository> [<directory>] [-- <gitflags>...]",
Short: "Clone a repository locally", Short: "Clone a repository locally",
Long: `Clone a repository with the configured Git protocol (git_protocol: https or ssh) and tea's Long: `Clone a repository with the configured Git protocol and tea's credentials. Git flags can
credentials. Git flags can follow --. follow --. A fork's parent is added as the "upstream" remote, and tea commands in the clone then
target the parent.`,
A fork's parent is added as the "upstream" remote, which its default branch pulls from while pushes
stay on the fork; tea commands in the clone then target the parent. A pull mirror's source is added
as the "source" remote.`,
Example: `# Clone a repository into ./project Example: `# Clone a repository into ./project
$ tea repo clone alice/project $ tea repo clone alice/project
@@ -304,12 +299,11 @@ func (a *application) repoForkCommand() *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
Use: "fork [<repository>] [-- <gitflags>...]", Use: "fork [<repository>] [-- <gitflags>...]",
Short: "Fork a repository", Short: "Fork a repository",
Long: `Fork a repository for your account, or into an organization with --org. When the account or Long: `Fork a repository for your account, or into an organization with --org. An existing fork is
organization already has a fork, tea reports it instead of failing. reported instead of failing.
With --clone, the fork is cloned, and its parent is added as the "upstream" remote; Git flags can --clone clones the fork (Git flags can follow --). Inside a clone of the repository, --remote adds
follow --. Inside a clone of the repository, --remote adds the fork as the "origin" remote (or the fork as "origin" and renames the current "origin" to "upstream".`,
--remote-name), renaming the current "origin" to "upstream".`,
Example: `# Fork the repository of the current clone and add the fork as origin Example: `# Fork the repository of the current clone and add the fork as origin
$ tea repo fork --remote $ tea repo fork --remote
@@ -502,18 +496,13 @@ func (a *application) inferredRepository(t target, verb string, refuseYes bool)
if t.RepoSource == "--repo" { if t.RepoSource == "--repo" {
return "", nil return "", nil
} }
source := describeRepoSource(t.RepoSource) source := strings.Replace(t.RepoSource, "remote ", "git remote ", 1)
if refuseYes && a.options.yes { if refuseYes && a.options.yes {
return "", clierr.Wrap(clierr.Usage, fmt.Errorf("--yes requires an explicit repository, not one inferred from %s: tea repo %s %s --yes", source, verb, t.Repo)) return "", clierr.Wrap(clierr.Usage, fmt.Errorf("--yes requires an explicit repository, not one inferred from %s: tea repo %s %s --yes", source, verb, t.Repo))
} }
return " (from " + source + ")", nil return " (from " + source + ")", nil
} }
// describeRepoSource words a repository source for people: git remote upstream, TEA_REPO, ...
func describeRepoSource(source string) string {
return strings.Replace(source, "remote ", "git remote ", 1)
}
// repoArchiveCommand builds repo archive, or repo unarchive. // repoArchiveCommand builds repo archive, or repo unarchive.
func (a *application) repoArchiveCommand(archive bool) *cobra.Command { func (a *application) repoArchiveCommand(archive bool) *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
@@ -581,7 +570,7 @@ $ tea repo archive alice/old-project --yes`,
} }
func (a *application) repoRenameCommand() *cobra.Command { func (a *application) repoRenameCommand() *cobra.Command {
command := &cobra.Command{ return &cobra.Command{
Use: "rename <new-name>", Use: "rename <new-name>",
Short: "Rename a repository", Short: "Rename a repository",
Long: "Rename a repository; its owner stays the same. Gitea redirects the old name to the new one.", Long: "Rename a repository; its owner stays the same. Gitea redirects the old name to the new one.",
@@ -625,16 +614,13 @@ $ tea repo rename new-name -R alice/old-name`,
return a.jsonResult(cmd, item) return a.jsonResult(cmd, item)
}, },
} }
return command
} }
func (a *application) repoDeleteCommand() *cobra.Command { func (a *application) repoDeleteCommand() *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
Use: "delete [<repository>]", Use: "delete [<repository>]",
Short: "Delete a repository", Short: "Delete a repository",
Long: `Delete a repository with its issues, pull requests, releases and wiki. This cannot be undone. Long: "Delete a repository with its issues, pull requests, releases and wiki. This cannot be undone.",
Type the repository's name to confirm. With --yes, name the repository explicitly.`,
Example: `# Delete a repository after typing its name Example: `# Delete a repository after typing its name
$ tea repo delete alice/old-project $ tea repo delete alice/old-project
+2 -5
View File
@@ -61,10 +61,8 @@ func (a *application) repoSetDefaultCommand() *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
Use: "set-default [<repository>]", Use: "set-default [<repository>]",
Short: "Set the default repository for this clone", Short: "Set the default repository for this clone",
Long: `Set the repository that tea commands use in the current Git clone, instead of the one found through Long: `Set the repository that tea commands use in the current Git clone instead of the one of its Git
its Git remotes. It is saved as tea.repository in the clone's Git configuration. remotes (saved as tea.repository in the clone's Git configuration).`,
Without an argument, the repository that tea selects now is saved.`,
Example: `# Make tea commands in this clone use the fork's parent Example: `# Make tea commands in this clone use the fork's parent
$ tea repo set-default alice/project $ tea repo set-default alice/project
@@ -280,7 +278,6 @@ $ tea repo view alice/project --web`,
return view return view
} }
// repositoryView shows a repository with its README as the body.
func (a *application) repositoryView(item services.Repository, readme string) terminal.View { func (a *application) repositoryView(item services.Repository, readme string) terminal.View {
badges := []terminal.Cell{{Text: "public", Color: "green"}} badges := []terminal.Cell{{Text: "public", Color: "green"}}
if item.Private { if item.Private {
+6 -11
View File
@@ -36,11 +36,9 @@ func (a *application) repoCreateCommand() *cobra.Command {
Short: "Create a repository", Short: "Create a repository",
Long: `Create a repository for your account, or for an organization as ORG/NAME or with --org. Long: `Create a repository for your account, or for an organization as ORG/NAME or with --org.
A new repository is empty unless --auto-init adds a first commit with a README and, optionally, a --auto-init adds a first commit with a README, .gitignore and license; --template copies another
.gitignore and a license from the server's templates. --template copies another repository instead. repository. --source adds the new repository as a remote of a local Git repository, and --push
pushes its current branch there.`,
--source publishes an existing local Git repository: it adds the new repository as a remote of
it (origin unless --remote names another), and --push pushes the current branch there.`,
Example: `# Create a private repository with a README and an MIT license Example: `# Create a private repository with a README and an MIT license
$ tea repo create project --private --auto-init --license MIT $ tea repo create project --private --auto-init --license MIT
@@ -83,7 +81,7 @@ $ tea repo create project --source . --push`,
return err return err
} }
} }
prompted, err := a.requireInput(cmd, &opts.Name, "name", "Repository name", func(name string) error { return validateRepositoryName(name) }) prompted, err := a.requireInput(cmd, &opts.Name, "name", "Repository name", validateRepositoryName)
if err != nil { if err != nil {
return err return err
} }
@@ -130,11 +128,8 @@ $ tea repo create project --source . --push`,
} }
var item services.Repository var item services.Repository
if template != "" { if template != "" {
templateOpts.Name = opts.Name templateOpts.Name, templateOpts.Owner, templateOpts.Description = opts.Name, org, opts.Description
templateOpts.Owner = org templateOpts.Private, templateOpts.DefaultBranch = opts.Private, opts.DefaultBranch
templateOpts.Description = opts.Description
templateOpts.Private = opts.Private
templateOpts.DefaultBranch = opts.DefaultBranch
item, err = service.Generate(cmd.Context(), target.Repo, templateOpts) item, err = service.Generate(cmd.Context(), target.Repo, templateOpts)
} else { } else {
item, err = service.Create(cmd.Context(), org, opts) item, err = service.Create(cmd.Context(), org, opts)
+5 -8
View File
@@ -44,12 +44,9 @@ func (a *application) repoMigrateCommand() *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
Use: "migrate [<clone-url>]", Use: "migrate [<clone-url>]",
Short: "Import a repository from another server", Short: "Import a repository from another server",
Long: `Import a repository into Gitea from a Git URL, or from another forge with --service, which also Long: `Import a repository from a Git URL, or from another forge with --service (which needs an access
copies its issues, labels, milestones, pull requests, releases or wiki on request. --mirror creates token and can also copy issues, pull requests, releases and more). --mirror creates a pull mirror
a pull mirror that Gitea updates from the source instead. instead. The name defaults to the last segment of the URL.`,
The repository is named after the last segment of the URL unless --name is given. Imports from a
forge with --service need an access token for it.`,
Example: `# Import a public Git repository Example: `# Import a public Git repository
$ tea repo migrate https://github.com/go-gitea/tea.git $ tea repo migrate https://github.com/go-gitea/tea.git
@@ -81,7 +78,7 @@ $ tea repo migrate https://github.com/acme/tool --service github --issues --pull
if err := validateMigrationFlags(cmd, opts, passwordFile, tokenFile); err != nil { if err := validateMigrationFlags(cmd, opts, passwordFile, tokenFile); err != nil {
return err return err
} }
validURL := func(raw string) error { return validateMigrationURL(raw) } validURL := validateMigrationURL
if cmd.Flags().Changed("server-path") { if cmd.Flags().Changed("server-path") {
validURL = nil validURL = nil
} }
@@ -95,7 +92,7 @@ $ tea repo migrate https://github.com/acme/tool --service github --issues --pull
} }
} }
opts.Name = cmp.Or(opts.Name, migrationName(opts.CloneURL)) opts.Name = cmp.Or(opts.Name, migrationName(opts.CloneURL))
named, err := a.requireInput(cmd, &opts.Name, "name", "Repository name", func(name string) error { return validateRepositoryName(name) }) named, err := a.requireInput(cmd, &opts.Name, "name", "Repository name", validateRepositoryName)
if err != nil { if err != nil {
return err return err
} }
+4 -7
View File
@@ -21,14 +21,11 @@ func (a *application) repositorySyncCommand() *cobra.Command {
command := &cobra.Command{ command := &cobra.Command{
Use: "sync [<repository>]", Use: "sync [<repository>]",
Short: "Sync a branch with its remote or its fork's parent", Short: "Sync a branch with its remote or its fork's parent",
Long: `Fast-forward a local branch to the branch of the same name on the repository's Git remote. Long: `Fast-forward a local branch (default: the repository's default branch) to the same branch on
the repository's Git remote, without switching branches. Diverged branches are left unchanged.
The branch defaults to the repository's default branch. It is created when it is missing, and it is Given a repository without a Git remote here, sync updates it on the server instead: a fork
never checked out: the branch you are on stays checked out. When the branch is checked out, the fast-forwards to its parent, and a pull mirror fetches its source.`,
working tree must be clean. Branches that have diverged from the remote are left unchanged.
Given a repository that has no remote in the current directory, sync updates it on the server
instead: a fork's branch is fast-forwarded to its parent's, and a pull mirror fetches its source.`,
Example: `# Fast-forward the default branch from the repository's remote Example: `# Fast-forward the default branch from the repository's remote
$ tea repo sync $ tea repo sync
+1 -8
View File
@@ -40,7 +40,6 @@ func parseTeam(raw string) (teamRef, error) {
return teamRef{}, clierr.Wrap(clierr.Usage, fmt.Errorf("invalid team %q: use ORG/TEAM, such as acme/developers, or a team ID", raw)) return teamRef{}, clierr.Wrap(clierr.Usage, fmt.Errorf("invalid team %q: use ORG/TEAM, such as acme/developers, or a team ID", raw))
} }
// find looks the team up on the instance.
func (r teamRef) find(ctx context.Context, service services.Teams) (services.Team, error) { func (r teamRef) find(ctx context.Context, service services.Teams) (services.Team, error) {
if r.org != "" { if r.org != "" {
return service.Find(ctx, r.org, r.name) return service.Find(ctx, r.org, r.name)
@@ -63,7 +62,6 @@ func (a *application) team(cmd *cobra.Command, raw string) (target, services.Tea
return t, service, team, err return t, service, team, err
} }
// teamURL is the page of a team on its instance.
func teamURL(t target, team services.Team) string { func teamURL(t target, team services.Team) string {
return webURL(t, "org", team.Organization, "teams", strings.ToLower(team.Name)) return webURL(t, "org", team.Organization, "teams", strings.ToLower(team.Name))
} }
@@ -284,7 +282,6 @@ func (f *teamFlags) unitPolicy(cmd *cobra.Command, create bool) (map[string]stri
return policy, nil return policy, nil
} }
// options returns the supplied team fields.
func (f *teamFlags) options(cmd *cobra.Command) services.TeamOptions { func (f *teamFlags) options(cmd *cobra.Command) services.TeamOptions {
opts := services.TeamOptions{} opts := services.TeamOptions{}
for _, field := range []struct { for _, field := range []struct {
@@ -436,10 +433,7 @@ func (a *application) teamDeleteCommand() *cobra.Command {
var force bool var force bool
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "delete <team>", Short: "Delete a team", Args: argsExact(1), ValidArgsFunction: a.completeTeams(), Use: "delete <team>", Short: "Delete a team", Args: argsExact(1), ValidArgsFunction: a.completeTeams(),
Long: `Delete a team. Its members lose the access the team gave them. Long: "Delete a team. Deleting an organization's owner team needs --force.",
Deleting an organization's owner team takes away owner access, so it needs --force and, on a
terminal, typing ORG/TEAM.`,
Example: `$ tea team delete acme/reviewers Example: `$ tea team delete acme/reviewers
$ tea team delete 7 --yes`, $ tea team delete 7 --yes`,
Annotations: map[string]string{"tea.text": "team deletion does not return a resource", "tea.confirm": "true", "tea.arguments": teamArguments}, Annotations: map[string]string{"tea.text": "team deletion does not return a resource", "tea.confirm": "true", "tea.arguments": teamArguments},
@@ -610,7 +604,6 @@ its organization. Repositories are named alone, such as service, or as ORG/NAME.
return group return group
} }
// teamRepoAccessCommand grants or revokes team access to repositories.
func (a *application) teamRepoAccessCommand(grant bool) *cobra.Command { func (a *application) teamRepoAccessCommand(grant bool) *cobra.Command {
verb, short, example := "remove", "Revoke team access to repositories", "$ tea team repo remove acme/developers legacy" verb, short, example := "remove", "Revoke team access to repositories", "$ tea team repo remove acme/developers legacy"
annotations := map[string]string{"tea.text": "team access changes do not return a resource", "tea.arguments": teamArguments, "tea.confirm": "true"} annotations := map[string]string{"tea.text": "team access changes do not return a resource", "tea.arguments": teamArguments, "tea.confirm": "true"}