Add tea repo create-from-template #408

Merged
lunny merged 8 commits from noerw/tea:add-generate-repo into master 2021-10-20 01:31:36 +00:00
13 changed files with 313 additions and 65 deletions
Showing only changes of commit 3eb709b112 - Show all commits

View File

@ -58,7 +58,7 @@
tea open milestones # open web ui for milestones
# send gitea desktop notifications every 5 minutes (bash + libnotify)
while :; do tea notifications --all -o simple | xargs -i notify-send {}; sleep 300; done
while :; do tea notifications --mine -o simple | xargs -i notify-send {}; sleep 300; done
ABOUT
Written & maintained by The Gitea Authors.

View File

@ -5,9 +5,9 @@
package cmd
import (
"fmt"
"code.gitea.io/tea/cmd/organizations"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v2"
)
@ -23,18 +23,26 @@ var CmdOrgs = cli.Command{
Action: runOrganizations,
Subcommands: []*cli.Command{
&organizations.CmdOrganizationList,
&organizations.CmdOrganizationCreate,
&organizations.CmdOrganizationDelete,
},
Flags: organizations.CmdOrganizationList.Flags,
}
func runOrganizations(ctx *cli.Context) error {
func runOrganizations(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
if ctx.Args().Len() == 1 {
return runOrganizationDetail(ctx.Args().First())
return runOrganizationDetail(ctx)
}
return organizations.RunOrganizationList(ctx)
return organizations.RunOrganizationList(cmd)
}
func runOrganizationDetail(path string) error {
return fmt.Errorf("Not yet implemented")
func runOrganizationDetail(ctx *context.TeaContext) error {
org, _, err := ctx.Login.Client().GetOrg(ctx.Args().First())
if err != nil {
return err
}
print.OrganizationDetails(org)
return nil
}

View File

@ -0,0 +1,90 @@
// Copyright 2021 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 organizations
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdOrganizationCreate represents a sub command of organizations to delete a given user organization
var CmdOrganizationCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create an organization",
Description: "Create an organization",
Action: RunOrganizationCreate,
ArgsUsage: "<organization name>",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"d"},
},
&cli.StringFlag{
Name: "website",
Aliases: []string{"w"},
},
&cli.StringFlag{
Name: "location",
Aliases: []string{"L"},
},
&cli.StringFlag{
Name: "visibility",
Aliases: []string{"v"},
},
&cli.BoolFlag{
Name: "repo-admins-can-change-team-access",
},
&flags.LoginFlag,
},
}
// RunOrganizationCreate sets up a new organization
func RunOrganizationCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
if ctx.Args().Len() < 1 {
return fmt.Errorf("You have to specify the organization name you want to create")
}
var visibility gitea.VisibleType
switch ctx.String("visibility") {
case "", "public":
visibility = gitea.VisibleTypePublic
case "private":
visibility = gitea.VisibleTypePrivate
case "limited":
visibility = gitea.VisibleTypeLimited
default:
return fmt.Errorf("unknown visibility '%s'", ctx.String("visibility"))
}
org, _, err := ctx.Login.Client().CreateOrg(gitea.CreateOrgOption{
Name: ctx.Args().First(),
// FullName: , // not really meaningful for orgs (not displayed in webui, use description instead?)
Description: ctx.String("description"),
Website: ctx.String("website"),
Location: ctx.String("location"),
RepoAdminChangeTeamAccess: ctx.Bool("repo-admins-can-change-team-access"),
Visibility: visibility,
})
if err != nil {
return err
}
print.OrganizationDetails(org)
return err
}

View File

@ -13,6 +13,10 @@ import (
"github.com/urfave/cli/v2"
)
var pullFieldsFlag = flags.FieldsFlag(print.PullFields, []string{
"index", "title", "state", "author", "milestone", "updated", "labels",
})
// CmdPullsList represents a sub command of issues to list pulls
var CmdPullsList = cli.Command{
Name: "list",
@ -20,7 +24,7 @@ var CmdPullsList = cli.Command{
Usage: "List pull requests of the repository",
Description: `List pull requests of the repository`,
Action: RunPullsList,
Flags: flags.IssuePRFlags,
Flags: append([]cli.Flag{pullFieldsFlag}, flags.IssuePRFlags...),
}
// RunPullsList return list of pulls
@ -46,6 +50,11 @@ func RunPullsList(cmd *cli.Context) error {
return err
}
print.PullsList(prs, ctx.Output)
fields, err := pullFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
print.PullsList(prs, ctx.Output, fields)
return nil
}

View File

@ -79,6 +79,14 @@ var CmdRepoCreate = cli.Command{
Required: false,
Usage: "use custom default branch (need --init)",
},
&cli.BoolFlag{
Name: "template",
Usage: "make repo a template repo",
},
&cli.StringFlag{
Name: "trustmodel",
Usage: "select trust model (committer,collaborator,collaborator+committer)",
},
}, flags.LoginOutputFlags...),
}
@ -86,9 +94,24 @@ func runRepoCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
var (
repo *gitea.Repository
err error
repo *gitea.Repository
err error
trustmodel gitea.TrustModel
)
if ctx.IsSet("trustmodel") {
switch ctx.String("trustmodel") {
case "committer":
trustmodel = gitea.TrustModelCommitter
case "collaborator":
trustmodel = gitea.TrustModelCollaborator
case "collaborator+committer":
trustmodel = gitea.TrustModelCollaboratorCommitter
default:
return fmt.Errorf("unknown trustmodel type '%s'", ctx.String("trustmodel"))
}
}
opts := gitea.CreateRepoOption{
Name: ctx.String("name"),
Description: ctx.String("description"),
@ -99,6 +122,8 @@ func runRepoCreate(cmd *cli.Context) error {
License: ctx.String("license"),
Readme: ctx.String("readme"),
DefaultBranch: ctx.String("branch"),
Template: ctx.Bool("template"),
TrustModel: trustmodel,
}
if len(ctx.String("owner")) != 0 {
repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts)

View File

@ -108,7 +108,7 @@ var helpTemplate = bold(`
tea open milestones # open web ui for milestones
# send gitea desktop notifications every 5 minutes (bash + libnotify)
while :; do tea notifications --all -o simple | xargs -i notify-send {}; sleep 300; done
while :; do tea notifications --mine -o simple | xargs -i notify-send {}; sleep 300; done
ABOUT
Written & maintained by The Gitea Authors.

View File

@ -72,3 +72,16 @@ func formatUserName(u *gitea.User) string {
}
return u.FullName
}
func formatBoolean(b bool, allowIcons bool) string {
if !allowIcons {
return fmt.Sprintf("%v", b)
}
styled := "✔"
if !b {
styled = "✖"
}
return styled
}

View File

@ -54,19 +54,20 @@ var IssueFields = []string{
func printIssues(issues []*gitea.Issue, output string, fields []string) {
labelMap := map[int64]string{}
var printables = make([]printable, len(issues))
machineReadable := isMachineReadable(output)
for i, x := range issues {
// pre-serialize labels for performance
for _, label := range x.Labels {
if _, ok := labelMap[label.ID]; !ok {
labelMap[label.ID] = formatLabel(label, !isMachineReadable(output), "")
labelMap[label.ID] = formatLabel(label, !machineReadable, "")
}
}
// store items with printable interface
printables[i] = &printableIssue{x, &labelMap}
}
t := tableFromItems(fields, printables)
t := tableFromItems(fields, printables, machineReadable)
t.print(output)
}
@ -75,7 +76,7 @@ type printableIssue struct {
formattedLabels *map[int64]string
}
func (x printableIssue) FormatField(field string) string {
func (x printableIssue) FormatField(field string, machineReadable bool) string {
switch field {
case "index":
return fmt.Sprintf("%d", x.Index)

View File

@ -10,6 +10,18 @@ import (
"code.gitea.io/sdk/gitea"
)
// OrganizationDetails prints details of an org with formatting
func OrganizationDetails(org *gitea.Organization) {
outputMarkdown(fmt.Sprintf(
"# %s\n%s\n\n- Visibility: %s\n- Location: %s\n- Website: %s\n",
org.UserName,
org.Description,
org.Visibility,
org.Location,
org.Website,
), "")
}
// OrganizationsList prints a listing of the organizations
func OrganizationsList(organizations []*gitea.Organization, output string) {
if len(organizations) == 0 {

View File

@ -6,7 +6,6 @@ package print
import (
"fmt"
"strconv"
"strings"
"code.gitea.io/sdk/gitea"
@ -23,19 +22,8 @@ var ciStatusSymbols = map[gitea.StatusState]string{
// PullDetails print an pull rendered to stdout
func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *gitea.CombinedStatus) {
base := pr.Base.Name
head := pr.Head.Name
if pr.Head.RepoID != pr.Base.RepoID {
if pr.Head.Repository != nil {
head = pr.Head.Repository.Owner.UserName + ":" + head
} else {
head = "delete:" + head
}
}
state := pr.State
if pr.Merged != nil {
state = "merged"
}
head := formatPRHead(pr)
state := formatPRState(pr)
out := fmt.Sprintf(
"# #%d %s (%s)\n@%s created %s\t**%s** <- **%s**\n\n%s\n\n",
@ -79,6 +67,25 @@ func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *g
outputMarkdown(out, pr.HTMLURL)
}
func formatPRHead(pr *gitea.PullRequest) string {
head := pr.Head.Name
if pr.Head.RepoID != pr.Base.RepoID {
if pr.Head.Repository != nil {
head = pr.Head.Repository.Owner.UserName + ":" + head
} else {
head = "delete:" + head
}
}
return head
}
func formatPRState(pr *gitea.PullRequest) string {
if pr.Merged != nil {
return "merged"
}
return string(pr.State)
}
func formatReviews(reviews []*gitea.PullReview) string {
result := ""
if len(reviews) == 0 {
@ -114,37 +121,120 @@ func formatReviews(reviews []*gitea.PullReview) string {
}
// PullsList prints a listing of pulls
func PullsList(prs []*gitea.PullRequest, output string) {
t := tableWithHeader(
"Index",
"Title",
"State",
"Author",
"Milestone",
"Updated",
)
func PullsList(prs []*gitea.PullRequest, output string, fields []string) {
printPulls(prs, output, fields)
}
for _, pr := range prs {
if pr == nil {
continue
// PullFields are all available fields to print with PullsList()
var PullFields = []string{
"index",
"state",
"author",
"author-id",
"url",
"title",
"body",
"mergeable",
"base",
"base-commit",
"head",
"diff",
"patch",
"created",
"updated",
"deadline",
"assignees",
"milestone",
"labels",
"comments",
}
func printPulls(pulls []*gitea.PullRequest, output string, fields []string) {
labelMap := map[int64]string{}
var printables = make([]printable, len(pulls))
machineReadable := isMachineReadable(output)
for i, x := range pulls {
// pre-serialize labels for performance
for _, label := range x.Labels {
if _, ok := labelMap[label.ID]; !ok {
labelMap[label.ID] = formatLabel(label, !machineReadable, "")
}
}
author := pr.Poster.FullName
if len(author) == 0 {
author = pr.Poster.UserName
}
mile := ""
if pr.Milestone != nil {
mile = pr.Milestone.Title
}
t.addRow(
strconv.FormatInt(pr.Index, 10),
pr.Title,
string(pr.State),
author,
mile,
FormatTime(*pr.Updated),
)
// store items with printable interface
printables[i] = &printablePull{x, &labelMap}
}
t := tableFromItems(fields, printables, machineReadable)
t.print(output)
}
type printablePull struct {
*gitea.PullRequest
formattedLabels *map[int64]string
}
func (x printablePull) FormatField(field string, machineReadable bool) string {
switch field {
case "index":
return fmt.Sprintf("%d", x.Index)
case "state":
return formatPRState(x.PullRequest)
case "author":
return formatUserName(x.Poster)
case "author-id":
return x.Poster.UserName
case "url":
return x.HTMLURL
case "title":
return x.Title
case "body":
return x.Body
case "created":
return FormatTime(*x.Created)
case "updated":
return FormatTime(*x.Updated)
case "deadline":
if x.Deadline == nil {
return ""
}
return FormatTime(*x.Deadline)
case "milestone":
if x.Milestone != nil {
return x.Milestone.Title
}
return ""
case "labels":
var labels = make([]string, len(x.Labels))
for i, l := range x.Labels {
labels[i] = (*x.formattedLabels)[l.ID]
}
return strings.Join(labels, " ")
case "assignees":
var assignees = make([]string, len(x.Assignees))
for i, a := range x.Assignees {
assignees[i] = formatUserName(a)
}
return strings.Join(assignees, " ")
case "comments":
return fmt.Sprintf("%d", x.Comments)
case "mergeable":
isMergeable := x.Mergeable && x.State == gitea.StateOpen
return formatBoolean(isMergeable, !machineReadable)
case "base":
return x.Base.Ref
case "base-commit":
return x.MergeBase
case "head":
return formatPRHead(x.PullRequest)
case "diff":
return x.DiffURL
case "patch":
return x.PatchURL
}
return ""
}

View File

@ -18,7 +18,7 @@ func ReposList(repos []*gitea.Repository, output string, fields []string) {
for i, r := range repos {
printables[i] = &printableRepo{r}
}
t := tableFromItems(fields, printables)
t := tableFromItems(fields, printables, isMachineReadable(output))
t.print(output)
}
@ -107,7 +107,7 @@ var RepoFields = []string{
type printableRepo struct{ *gitea.Repository }
func (x printableRepo) FormatField(field string) string {
func (x printableRepo) FormatField(field string, machineReadable bool) string {
switch field {
case "description":
return x.Description

View File

@ -24,16 +24,16 @@ type table struct {
// printable can be implemented for structs to put fields dynamically into a table
type printable interface {
FormatField(field string) string
FormatField(field string, machineReadable bool) string
}
// high level api to print a table of items with dynamic fields
func tableFromItems(fields []string, values []printable) table {
func tableFromItems(fields []string, values []printable, machineReadable bool) table {
t := table{headers: fields}
for _, v := range values {
row := make([]string, len(fields))
for i, f := range fields {
row[i] = v.FormatField(f)
row[i] = v.FormatField(f, machineReadable)
}
t.addRowSlice(row)
}

View File

@ -18,7 +18,7 @@ func TrackedTimesList(times []*gitea.TrackedTime, outputType string, fields []st
totalDuration += t.Time
printables[i] = &printableTrackedTime{t, outputType}
}
t := tableFromItems(fields, printables)
t := tableFromItems(fields, printables, isMachineReadable(outputType))
if printTotal {
total := make([]string, len(fields))
@ -45,7 +45,7 @@ type printableTrackedTime struct {
outputFormat string
}
func (t printableTrackedTime) FormatField(field string) string {
func (t printableTrackedTime) FormatField(field string, machineReadable bool) string {
switch field {
case "id":
return fmt.Sprintf("%d", t.ID)