Add Repo Create subcomand & enhancements #173

Merged
6543 merged 20 commits from 6543/tea:repos-create into master 2020-09-23 19:50:02 +00:00
2 changed files with 255 additions and 51 deletions

View File

@ -5,26 +5,32 @@
package cmd
import (
"fmt"
"log"
"net/http"
"strings"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdRepos represents to login a gitea server.
var CmdRepos = cli.Command{
Name: "repos",
Outdated
Review

"Show repository details"
Please change Description field as well

"Show repository details" Please change `Description` field as well
Usage: "Operate with repositories",
Description: `Operate with repositories`,
Action: runReposList,
Usage: "Show repositories details",
Description: "Show repositories details",
ArgsUsage: "[<repo owner>/<repo name>]",
Action: runRepos,
Subcommands: []*cli.Command{
&CmdReposList,
&CmdRepoCreate,
},
Flags: LoginOutputFlags,
}
// CmdReposList represents a sub command of issues to list issues
// CmdReposList represents a sub command of repos to list them
var CmdReposList = cli.Command{
Name: "ls",
Usage: "List available repositories",
@ -32,66 +38,164 @@ var CmdReposList = cli.Command{
Action: runReposList,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "mode",
Usage: "Filter listed repositories based on mode, optional - fork, mirror, source",
Name: "mode",
Aliases: []string{"m"},
Required: false,
Usage: "Filter by mode: fork, mirror, source",
},
&cli.StringFlag{
Name: "org",
Usage: "Filter listed repositories based on organization, optional",
Name: "owner",
Outdated
Review

lets use owner / -O everywhere, and avoid two terms for the same concept

lets use `owner` / `-O` everywhere, and avoid two terms for the same concept
Outdated
Review

And why are there two flags org and user? Can't they be merged into --owner?

And why are there two flags `org` and `user`? Can't they be merged into `--owner`?
Outdated
Review

It will add a request ...
but It's user convinient So why not ...

It will add a request ... but It's user convinient So why not ...
Outdated
Review

Hm, I consider two flags for the same field is more confusing than convenient, but idk.

Let's leave it as is, but please change the description to say "Filter by user" then

Hm, I consider two flags for the same field is more confusing than convenient, but idk. Let's leave it as is, but please change the description to say "Filter by user" then
Review

changed :)

changed :)
Aliases: []string{"O"},
Required: false,
Usage: "Filter by owner",
},
&cli.StringFlag{
Name: "user",
Usage: "Filter listed repositories absed on user, optional",
Name: "private",
Required: false,
Usage: "Filter private repos (true|false)",
},
&cli.StringFlag{
Name: "archived",
Required: false,
Usage: "Filter archived repos (true|false)",
},
}, LoginOutputFlags...),
}
// CmdRepoCreate represents a sub command of repos to create one
var CmdRepoCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create a repository",
Description: "Create a repository",
Action: runRepoCreate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{""},
Required: true,
Usage: "name of new repo",
},
&cli.StringFlag{
Name: "owner",
Aliases: []string{"O"},
Required: false,
Usage: "name of repo owner",
},
&cli.BoolFlag{
Name: "private",
Required: false,
Value: false,
Usage: "make repo private",
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"desc"},
Required: false,
Usage: "add description to repo",
},
&cli.BoolFlag{
Name: "init",
Required: false,
Value: false,
Usage: "initialize repo",
},
&cli.StringFlag{
Name: "labels",
Required: false,
Usage: "name of label set to add",
},
&cli.StringFlag{
Name: "gitignores",
Aliases: []string{"git"},
Required: false,
Usage: "list of gitignore templates (need --init)",
},
&cli.StringFlag{
Name: "license",
Required: false,
Usage: "add license (need --init)",
},
&cli.StringFlag{
Name: "readme",
Required: false,
Usage: "use readme template (need --init)",
},
&cli.StringFlag{
Name: "branch",
Required: false,
Usage: "use custom default branch (need --init)",
},
}, LoginOutputFlags...),
}
func runRepos(ctx *cli.Context) error {
if ctx.Args().Len() == 1 {
return runRepoDetail(ctx, ctx.Args().First())
}
return runReposList(ctx)
}
// runReposList list repositories
func runReposList(ctx *cli.Context) error {
login := initCommandLoginOnly()
client := login.Client()
mode := ctx.String("mode")
org := ctx.String("org")
user := ctx.String("user")
var rps []*gitea.Repository
var err error
if org != "" {
rps, _, err = login.Client().ListOrgRepos(org, gitea.ListOrgReposOptions{})
} else if user != "" {
rps, _, err = login.Client().ListUserRepos(user, gitea.ListReposOptions{})
var ownerID int64
if ctx.IsSet("owner") {
6543 marked this conversation as resolved Outdated

TODO: is usually better detected by IDEs

`TODO:` is usually better detected by IDEs
Outdated
Review

By using initCommandLoginOnly(), the login is not read from the current repo!

This came up in #189 as well, where I wrote a workaround.
But we need a proper solution for cases where the command is meaningful when executed outside of a repo, but we still want it to consider repo context where available.

By using `initCommandLoginOnly()`, the login is not read from the current repo! This came up in #189 as well, where I wrote a workaround. But we need a proper solution for cases where the command is meaningful when executed outside of a repo, but we still want it to consider repo context where available.
Outdated
Review

If we use login, _, _ := initCommand() outside of a repo we will get this:

Error: repository does not exist

If we use `login, _, _ := initCommand()` outside of a repo we will get this: Error: repository does not exist
Outdated
Review

So yes we somehow have to unify both commands but in a way it will work for all commands

So yes we somehow have to unify both commands but in a way it will work for all commands
Outdated
Review

... witch is no mouch for this pull

... witch is no mouch for this pull
// test if owner is a organisation
org, resp, err := client.GetOrg(ctx.String("owner"))
if err != nil {
if resp == nil || resp.StatusCode != http.StatusNotFound {
return err
}
// if owner is no org, its a user
user, _, err := client.GetUserInfo(ctx.String("owner"))
if err != nil {
return err
}
ownerID = user.ID
} else {
ownerID = org.ID
}
} else {
rps, _, err = login.Client().ListMyRepos(gitea.ListReposOptions{})
me, _, err := client.GetMyUserInfo()
if err != nil {
return err
}
ownerID = me.ID
}
var isArchived *bool
if ctx.IsSet("archived") {
archived := strings.ToLower(ctx.String("archived"))[:1] == "t"
isArchived = &archived
}
var isPrivate *bool
if ctx.IsSet("private") {
private := strings.ToLower(ctx.String("private"))[:1] == "t"
isArchived = &private
}
mode := gitea.RepoTypeNone
switch ctx.String("mode") {
case "fork":
mode = gitea.RepoTypeFork
case "mirror":
mode = gitea.RepoTypeMirror
case "source":
mode = gitea.RepoTypeSource
}
rps, _, err := client.SearchRepos(gitea.SearchRepoOptions{
OwnerID: ownerID,
IsPrivate: isPrivate,
IsArchived: isArchived,
Type: mode,
})
if err != nil {
log.Fatal(err)
}
var repos []*gitea.Repository
if mode == "" {
repos = rps
} else if mode == "fork" {
for _, rp := range rps {
if rp.Fork == true {
repos = append(repos, rp)
}
}
} else if mode == "mirror" {
for _, rp := range rps {
if rp.Mirror == true {
repos = append(repos, rp)
}
}
} else if mode == "source" {
for _, rp := range rps {
if rp.Mirror != true && rp.Fork != true {
repos = append(repos, rp)
}
}
} else {
log.Fatal("Unknown mode: ", mode, "\nUse one of the following:\n- fork\n- mirror\n- source\n")
return nil
return err
}
if len(rps) == 0 {
@ -107,7 +211,7 @@ func runReposList(ctx *cli.Context) error {
}
var values [][]string
for _, rp := range repos {
for _, rp := range rps {
var mode = "source"
if rp.Fork {
mode = "fork"
@ -130,3 +234,80 @@ func runReposList(ctx *cli.Context) error {
return nil
}
func runRepoDetail(_ *cli.Context, path string) error {
login := initCommandLoginOnly()
client := login.Client()
repoOwner, repoName := getOwnerAndRepo(path, login.User)
repo, _, err := client.GetRepo(repoOwner, repoName)
if err != nil {
return err
}
topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
if err != nil {
return err
}
output := repo.FullName
if repo.Mirror {
output += " (mirror)"
}
if repo.Fork {
output += " (fork)"
}
if repo.Archived {
output += " (archived)"
}
if repo.Empty {
output += " (empty)"
}
output += "\n"
if len(topics) != 0 {
output += "Topics: " + strings.Join(topics, ", ") + "\n"
}
output += "\n"
output += repo.Description + "\n\n"
output += fmt.Sprintf(
"Open Issues: %d, Stars: %d, Forks: %d, Size: %s\n\n",
repo.OpenIssues,
repo.Stars,
repo.Forks,
utils.FormatSize(int64(repo.Size)),
)
fmt.Print(output)
return nil
}
func runRepoCreate(ctx *cli.Context) error {
login := initCommandLoginOnly()
client := login.Client()
var (
repo *gitea.Repository
err error
)
opts := gitea.CreateRepoOption{
Name: ctx.String("name"),
Description: ctx.String("description"),
Private: ctx.Bool("private"),
AutoInit: ctx.Bool("init"),
IssueLabels: ctx.String("labels"),
Gitignores: ctx.String("gitignores"),
License: ctx.String("license"),
Readme: ctx.String("readme"),
DefaultBranch: ctx.String("branch"),
}
if len(ctx.String("owner")) != 0 {
repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts)
} else {
repo, _, err = client.CreateRepo(opts)
}
if err != nil {
return err
}
if err = runRepoDetail(ctx, repo.FullName); err != nil {
return err
}
fmt.Printf("%s\n", repo.HTMLURL)
return nil
}

23
modules/utils/format.go Normal file
View File

@ -0,0 +1,23 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package utils
import "fmt"
// FormatSize get kb in int and return string
func FormatSize(kb int64) string {
if kb < 1024 {
return fmt.Sprintf("%d Kb", kb)
}
mb := kb / 1024
if mb < 1024 {
return fmt.Sprintf("%d Mb", mb)
}
gb := mb / 1024
if gb < 1024 {
return fmt.Sprintf("%d Gb", gb)
}
return fmt.Sprintf("%d Tb", gb/1024)
}