mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-27 03:21:25 +01:00
fix(repo): README lookup like Gitea, clearer branch/org errors, clone-after-create report, mirror interval 0 and search sort (follow gh)
Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
16 files changed
+211
-37
No files matched your search
@@ -385,6 +385,20 @@ func TestInstanceCompletions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteArgumentBranchesUsesTheNamedRepository(t *testing.T) {
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "/api/v1/repos/bob/tool/branches", r.URL.Path)
|
||||
_, _ = w.Write([]byte(`[{"name":"develop"}]`))
|
||||
}))
|
||||
defer server.Close()
|
||||
deps, _, _ := serverDeps(t, server.URL)
|
||||
a := &application{deps: deps}
|
||||
cmd := &cobra.Command{}
|
||||
cmd.SetContext(context.Background())
|
||||
values, _ := a.completeArgumentBranches()(cmd, []string{"bob/tool"}, "")
|
||||
require.Equal(t, []string{"develop"}, values)
|
||||
}
|
||||
|
||||
func TestCompletionInstallHelp(t *testing.T) {
|
||||
deps, out, _ := testDeps(t)
|
||||
require.NoError(t, Execute(context.Background(), deps, []string{"completion", "bash", "--help"}))
|
||||
|
||||
@@ -413,15 +413,29 @@ func (a *application) completeUsers() cobra.CompletionFunc {
|
||||
|
||||
// completeBranches completes the local branches, then the repository's branches.
|
||||
func (a *application) completeBranches() cobra.CompletionFunc {
|
||||
return mergeCompletions(a.completeLocalBranches, a.apiCompletion(true, func(ctx context.Context, t target, _ string) ([]cobra.Completion, error) {
|
||||
items, err := services.Branches{Client: t.client}.ListPage(ctx, t.Repo, completionLimit, 1)
|
||||
return describe(items.Items, func(item services.Branch) (string, string) {
|
||||
if item.Protected {
|
||||
return item.Name, "protected"
|
||||
}
|
||||
return item.Name, ""
|
||||
}), err
|
||||
}))
|
||||
return mergeCompletions(a.completeLocalBranches, a.apiCompletion(true, listBranches))
|
||||
}
|
||||
|
||||
// completeArgumentBranches is completeBranches for a command with a positional [<repository>]:
|
||||
// once that argument is given, it completes that repository's branches, not those of the clone here.
|
||||
func (a *application) completeArgumentBranches() cobra.CompletionFunc {
|
||||
inClone := a.completeBranches()
|
||||
return func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 || a.options.repo != "" {
|
||||
return inClone(cmd, args, toComplete)
|
||||
}
|
||||
return a.apiCompletionIn(args[0], true, listBranches)(cmd, args, toComplete)
|
||||
}
|
||||
}
|
||||
|
||||
func listBranches(ctx context.Context, t target, _ string) ([]cobra.Completion, error) {
|
||||
items, err := services.Branches{Client: t.client}.ListPage(ctx, t.Repo, completionLimit, 1)
|
||||
return describe(items.Items, func(item services.Branch) (string, string) {
|
||||
if item.Protected {
|
||||
return item.Name, "protected"
|
||||
}
|
||||
return item.Name, ""
|
||||
}), err
|
||||
}
|
||||
|
||||
// completeTags completes the repository's tags.
|
||||
|
||||
@@ -281,6 +281,7 @@ func TestWriteTable(t *testing.T) {
|
||||
stderr: "more results available; use --page 3\n",
|
||||
},
|
||||
{name: "empty message on a terminal", tty: true, page: listPage{Number: 1}, stderr: "no open issues in alice/x\n"},
|
||||
{name: "next-page hint instead of the empty message on a filtered page", tty: true, page: listPage{Number: 1, More: true}, stderr: "more results available; use --page 2\n"},
|
||||
{name: "nothing when empty and piped", page: listPage{Number: 1}},
|
||||
{name: "next-page hint when an empty page has more", tty: true, page: listPage{Number: 1, More: true}, stderr: "more results available; use --page 2\n"},
|
||||
{name: "next-page hint when an empty page has more and piped", page: listPage{Number: 1, More: true}, stderr: "more results available; use --page 2\n"},
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitea.dev/tea/internal/clierr"
|
||||
"gitea.dev/tea/internal/terminal"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -52,5 +55,9 @@ func (a *application) requireInput(cmd *cobra.Command, value *string, flag, labe
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if hidden := cmd.Flags().Lookup(flag); *value == "" && hidden != nil && hidden.Hidden && !a.policy(s).CanPrompt(a.deps.Streams) {
|
||||
// --help does not list the flag, so point at the argument it stands in for.
|
||||
return false, clierr.Wrap(clierr.Usage, fmt.Errorf("missing required input: %s (pass it as an argument)", label))
|
||||
}
|
||||
return terminal.RequireInput(cmd.Context(), value, flag, label, validate, a.deps.Streams, a.policy(s), a.deps.Prompter)
|
||||
}
|
||||
@@ -273,7 +273,7 @@ $ tea repo view alice/project --web`,
|
||||
}
|
||||
view.Flags().BoolVarP(&web, "web", "w", false, "Open the repository in the browser")
|
||||
view.Flags().StringVarP(&branch, "branch", "b", "", "Show the README of this branch (default: the default branch)")
|
||||
_ = view.RegisterFlagCompletionFunc("branch", a.completeBranches())
|
||||
_ = view.RegisterFlagCompletionFunc("branch", a.completeArgumentBranches())
|
||||
view.ValidArgsFunction = a.completeRepos()
|
||||
return view
|
||||
}
|
||||
|
||||
@@ -147,7 +147,14 @@ $ tea repo create project --source . --push`,
|
||||
target.Repo = item.FullName
|
||||
switch {
|
||||
case clone:
|
||||
_, err = a.cloneRepository(cmd, target, item, cloneOptions{upstream: "upstream"})
|
||||
// Record the new repository first so a failed clone does not hide that it exists.
|
||||
if err := a.created(cmd, item, item.URL); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := a.cloneRepository(cmd, target, item, cloneOptions{upstream: "upstream"}); err != nil {
|
||||
return fmt.Errorf("created %s, but could not clone it: %w", item.FullName, err)
|
||||
}
|
||||
return nil
|
||||
case localSource != "":
|
||||
err = a.publishRepository(cmd, target, item, localSource, remote, push)
|
||||
}
|
||||
|
||||
@@ -63,6 +63,18 @@ func TestRepositoryCreateClonesNewRepository(t *testing.T) {
|
||||
require.JSONEq(t, `{"id":7,"full_name":"alice/project"}`, out.String())
|
||||
}
|
||||
|
||||
func TestRepositoryCreateReportsFailedCloneAfterCreating(t *testing.T) {
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = fmt.Fprintf(w, `{"id":7,"name":"project","full_name":"alice/project","clone_url":%q}`, "http://"+r.Host+"/alice/project.git")
|
||||
}))
|
||||
defer server.Close()
|
||||
deps, out, _ := serverDeps(t, server.URL)
|
||||
deps.Git = &fakeGit{}
|
||||
err := Execute(context.Background(), deps, []string{"repo", "create", "project", "--clone", "--no-input", "--json=full_name"})
|
||||
require.ErrorContains(t, err, "created alice/project, but could not clone it")
|
||||
require.JSONEq(t, `{"full_name":"alice/project"}`, out.String())
|
||||
}
|
||||
|
||||
func TestRepositoryCreateValidatesTemplatesBeforeCreating(t *testing.T) {
|
||||
var requests []string
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -108,6 +120,17 @@ func TestRepositoryCreatePublishesALocalRepository(t *testing.T) {
|
||||
require.ErrorContains(t, err, "--source publishes a local repository")
|
||||
}
|
||||
|
||||
func TestRepositoryMigrateAcceptsZeroMirrorInterval(t *testing.T) {
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requirePayload(t, `{"repo_name":"tea","clone_addr":"https://github.com/go-gitea/tea.git","service":"git","mirror":true,"mirror_interval":"0"}`, r.Body)
|
||||
_, _ = w.Write([]byte(`{"full_name":"alice/tea","mirror":true,"html_url":"https://gitea.test/alice/tea"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
deps, _, diagnostics := repoTTYDeps(t, server.URL)
|
||||
require.NoError(t, Execute(context.Background(), deps, []string{"repo", "migrate", "https://github.com/go-gitea/tea.git", "--mirror", "--mirror-interval", "0"}))
|
||||
require.Equal(t, "✓ Imported https://github.com/go-gitea/tea.git into alice/tea (pull mirror, synced manually)\n", diagnostics.String())
|
||||
}
|
||||
|
||||
func TestRepositoryMigrateNamesTheRepositoryAfterItsSource(t *testing.T) {
|
||||
for _, test := range []struct{ source, want string }{
|
||||
{"https://github.com/go-gitea/tea.git", "tea"},
|
||||
|
||||
@@ -133,7 +133,10 @@ $ tea repo migrate https://github.com/acme/tool --service github --issues --pull
|
||||
return err
|
||||
}
|
||||
detail := ""
|
||||
interval, _ := time.ParseDuration(opts.MirrorInterval)
|
||||
switch {
|
||||
case item.Mirror && opts.MirrorInterval != "" && interval == 0:
|
||||
detail = " (pull mirror, synced manually)"
|
||||
case item.Mirror && opts.MirrorInterval != "":
|
||||
detail = " (pull mirror, every " + opts.MirrorInterval + ")"
|
||||
case item.Mirror:
|
||||
@@ -161,7 +164,7 @@ $ tea repo migrate https://github.com/acme/tool --service github --issues --pull
|
||||
flags.BoolVar(&opts.PullRequests, "pull-requests", false, "Copy pull requests")
|
||||
flags.BoolVar(&opts.Releases, "releases", false, "Copy releases")
|
||||
flags.BoolVar(&opts.Milestones, "milestones", false, "Copy milestones")
|
||||
flags.StringVar(&opts.MirrorInterval, "mirror-interval", "", "Mirror interval, for example 8h")
|
||||
flags.StringVar(&opts.MirrorInterval, "mirror-interval", "", "Mirror interval, for example 8h (0 turns off periodic sync)")
|
||||
flags.BoolVar(&opts.LFS, "lfs", false, "Copy LFS objects")
|
||||
flags.StringVar(&opts.LFSEndpoint, "lfs-endpoint", "", "Source LFS endpoint")
|
||||
flags.StringVar(&opts.AuthUser, "auth-user", "", "Source authentication username")
|
||||
@@ -201,8 +204,8 @@ func validateMigrationFlags(cmd *cobra.Command, opts services.RepositoryMigratio
|
||||
}
|
||||
if opts.MirrorInterval != "" {
|
||||
interval, err := time.ParseDuration(opts.MirrorInterval)
|
||||
if err != nil || interval <= 0 || !opts.Mirror {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--mirror-interval requires --mirror and a positive duration"))
|
||||
if err != nil || interval < 0 || !opts.Mirror {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--mirror-interval requires --mirror and a duration of 0 or more"))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -100,7 +100,7 @@ $ tea repo sync bob/project`,
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&branch, "branch", "b", "", "Branch to sync (default: the repository's default branch)")
|
||||
_ = command.RegisterFlagCompletionFunc("branch", a.completeBranches())
|
||||
_ = command.RegisterFlagCompletionFunc("branch", a.completeArgumentBranches())
|
||||
command.ValidArgsFunction = a.completeRepos()
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ $ tea search repo go --topic --sort updated --order desc`,
|
||||
pageFlags(command, &opts.Limit, &opts.Page)
|
||||
flags.StringVar(&opts.Owner, "owner", "", "Only repositories of this user or organization")
|
||||
enumFlag(command, &opts.Kind, "type", "", "", "Only repositories of this kind", "fork", "source", "mirror")
|
||||
enumFlag(command, &opts.Sort, "sort", "", "alpha", "Sort by", "alpha", "created", "updated", "size", "id")
|
||||
enumFlag(command, &opts.Sort, "sort", "", "", "Sort by (default: alphabetical, your own repositories first)", "alpha", "created", "updated", "size", "id")
|
||||
enumFlag(command, &opts.Order, "order", "", "asc", "Sort order", "asc", "desc")
|
||||
flags.BoolVar(&opts.Topic, "topic", false, "Match repository topics")
|
||||
flags.BoolVar(&private, "private", false, "Only private repositories (use =false for public)")
|
||||
|
||||
@@ -152,7 +152,7 @@ func TestUsageErrorsPrecedeRequests(t *testing.T) {
|
||||
{[]string{"release", "edit", "v1", "--draft", "false"}, "--draft takes no value; use --draft or --draft=false"},
|
||||
{[]string{"release", "edit", "v1", "-d", "false", "-p", "False"}, "--draft takes no value; use --draft or --draft=false"},
|
||||
{[]string{"release", "edit", "v1", "v2", "--prerelease", "TRUE"}, "--prerelease takes no value; use --prerelease or --prerelease=false"},
|
||||
{[]string{"repo", "create"}, "missing required input: supply --name"},
|
||||
{[]string{"repo", "create"}, "missing required input: Repository name (pass it as an argument)"},
|
||||
{[]string{"repo", "create", "acme/bad/name"}, "repository name must be nonempty, at most 100 bytes, and contain no path separators or whitespace"},
|
||||
{[]string{"repo", "create", "name", "--name", "other"}, "supply name as an argument or --name, not both"},
|
||||
{[]string{"repo", "create", "name", "--default-branch", "bad..branch", "--auto-init"}, "invalid branch name"},
|
||||
@@ -175,11 +175,12 @@ func TestUsageErrorsPrecedeRequests(t *testing.T) {
|
||||
{[]string{"repo", "list", "--type", "invalid"}, "--type must be fork, source, or mirror"},
|
||||
{[]string{"repo", "set-default", "--json"}, "set-default configures Git"},
|
||||
{[]string{"repo", "sync", "--branch="}, "--branch cannot be empty"},
|
||||
{[]string{"repo", "migrate", "--name", "new"}, "missing required input: Clone URL (pass it as an argument)"},
|
||||
{[]string{"repo", "migrate", "https://source.test/repo", "--name", "new", "--clone-url", "https://other.test/repo"}, "supply clone URL as an argument or --clone-url, not both"},
|
||||
{[]string{"repo", "migrate", "https://user:secret@source.test/repo", "--name", "new"}, "clone URL must be an HTTP(S) or git URL without embedded credentials"},
|
||||
{[]string{"repo", "migrate", "https://source.test/repo", "--name", "new", "--service", "unknown"}, "--service must be git, gitea, gitlab, gogs, or github"},
|
||||
{[]string{"repo", "migrate", "https://source.test/repo", "--name", "new", "--issues"}, "plain Git migration cannot copy forge metadata"},
|
||||
{[]string{"repo", "migrate", "https://source.test/repo", "--name", "new", "--mirror-interval", "8h"}, "--mirror-interval requires --mirror and a positive duration"},
|
||||
{[]string{"repo", "migrate", "https://source.test/repo", "--name", "new", "--mirror-interval", "8h"}, "--mirror-interval requires --mirror and a duration of 0 or more"},
|
||||
{[]string{"repo", "migrate", "https://source.test/repo", "--name", "new", "--mirror", "--issues"}, "mirrors cannot copy issues, milestones, labels, pull requests, or releases"},
|
||||
{[]string{"repo", "migrate", "https://source.test/repo", "--name", "new", "--lfs-endpoint", "https://lfs.test"}, "--lfs-endpoint requires --lfs"},
|
||||
{[]string{"repo", "migrate", "https://source.test/repo", "--name", "new", "--auth-password", "secret"}, "--auth-password requires --auth-user"},
|
||||
|
||||
Reference in new issue
Block a user