From 1a1a186a22d6032cff5d7836c5a6ff3fec6d8eae Mon Sep 17 00:00:00 2001
From: Matti R
Date: Mon, 4 Oct 2021 16:25:17 -0400
Subject: [PATCH 1/6] Add user list command
---
cmd/admin.go | 24 ++++++++++++++++++
cmd/admin/users/list.go | 54 +++++++++++++++++++++++++++++++++++++++++
cmd/admin_users.go | 41 +++++++++++++++++++++++++++++++
cmd/categories.go | 1 +
main.go | 2 ++
modules/print/user.go | 3 +++
6 files changed, 125 insertions(+)
create mode 100644 cmd/admin.go
create mode 100644 cmd/admin/users/list.go
create mode 100644 cmd/admin_users.go
diff --git a/cmd/admin.go b/cmd/admin.go
new file mode 100644
index 0000000..c8640df
--- /dev/null
+++ b/cmd/admin.go
@@ -0,0 +1,24 @@
+// 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 cmd
+
+import (
+ "github.com/urfave/cli/v2"
+)
+
+// CmdAdmin represents the admin sub-commands
+var CmdAdmin = cli.Command{
+ Name: "admin",
+ Aliases: []string{"a"},
+ Category: catAdmin,
+ Action: func(cmd *cli.Context) error {
+ // TODO: this is just a stub for all admin actions
+ // there is no default admin action
+ return nil
+ },
+ Subcommands: []*cli.Command{
+ &cmdAdminUsers,
+ },
+}
diff --git a/cmd/admin/users/list.go b/cmd/admin/users/list.go
new file mode 100644
index 0000000..c29020c
--- /dev/null
+++ b/cmd/admin/users/list.go
@@ -0,0 +1,54 @@
+// 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 users
+
+import (
+ "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"
+)
+
+var userFieldsFlag = flags.FieldsFlag(print.UserFields, []string{
+ "id", "login", "full_name", "email", "activated",
+})
+
+// CmdUserList represents a sub command of users to list users
+var CmdUserList = cli.Command{
+ Name: "list",
+ Aliases: []string{"ls"},
+ Usage: "List Users",
+ Description: "List users",
+ Action: RunUserList,
+ Flags: append([]cli.Flag{
+ userFieldsFlag,
+ &flags.PaginationPageFlag,
+ &flags.PaginationLimitFlag,
+ }, flags.AllDefaultFlags...),
+}
+
+// RunUserList list user organizations
+func RunUserList(cmd *cli.Context) error {
+ ctx := context.InitCommand(cmd)
+ client := ctx.Login.Client()
+
+ users, _, err := client.AdminListUsers(gitea.AdminListUsersOptions{
+ ListOptions: ctx.GetListOptions(),
+ })
+ if err != nil {
+ return err
+ }
+
+ fields, err := userFieldsFlag.GetValues(cmd)
+ if err != nil {
+ return err
+ }
+
+ print.UserList(users, ctx.Output, fields)
+
+ return nil
+}
diff --git a/cmd/admin_users.go b/cmd/admin_users.go
new file mode 100644
index 0000000..056c996
--- /dev/null
+++ b/cmd/admin_users.go
@@ -0,0 +1,41 @@
+// 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 cmd
+
+import (
+ "code.gitea.io/tea/cmd/admin/users"
+ "code.gitea.io/tea/modules/context"
+ "code.gitea.io/tea/modules/print"
+
+ "github.com/urfave/cli/v2"
+)
+
+var cmdAdminUsers = cli.Command{
+ Name: "user",
+ Aliases: []string{"u"},
+ Category: catAdmin,
+ Action: func(ctx *cli.Context) error {
+ if ctx.Args().Len() == 1 {
+ return runAdminUserDetail(ctx, ctx.Args().First())
+ }
+ return users.RunUserList(ctx)
+ },
+ Subcommands: []*cli.Command{
+ &users.CmdUserList,
+ },
+ Flags: users.CmdUserList.Flags,
+}
+
+func runAdminUserDetail(cmd *cli.Context, u string) error {
+ ctx := context.InitCommand(cmd)
+ client := ctx.Login.Client()
+ user, _, err := client.GetUserInfo(u)
+ if err != nil {
+ return err
+ }
+
+ print.UserDetails(user)
+ return nil
+}
diff --git a/cmd/categories.go b/cmd/categories.go
index 4b2088b..c368b5a 100644
--- a/cmd/categories.go
+++ b/cmd/categories.go
@@ -8,4 +8,5 @@ var (
catSetup = "SETUP"
catEntities = "ENTITIES"
catHelpers = "HELPERS"
+ catAdmin = "ADMIN"
)
diff --git a/main.go b/main.go
index 52735cf..130e893 100644
--- a/main.go
+++ b/main.go
@@ -49,6 +49,8 @@ func main() {
&cmd.CmdOpen,
&cmd.CmdNotifications,
+
+ &cmd.CmdAdmin,
}
app.EnableBashCompletion = true
err := app.Run(os.Args)
diff --git a/modules/print/user.go b/modules/print/user.go
index 301685a..21ef43b 100644
--- a/modules/print/user.go
+++ b/modules/print/user.go
@@ -77,6 +77,7 @@ var UserFields = []string{
"website",
"description",
"visibility",
+ "activated",
}
type printableUser struct{ *gitea.User }
@@ -113,6 +114,8 @@ func (x printableUser) FormatField(field string, machineReadable bool) string {
return formatBoolean(x.Restricted, !machineReadable)
case "prohibit_login":
return formatBoolean(x.ProhibitLogin, !machineReadable)
+ case "activated":
+ return formatBoolean(x.IsActive, !machineReadable)
case "location":
return x.Location
case "website":
--
2.40.1
From 340f8b721f81d14e1743a67b10d33d602bf48bc1 Mon Sep 17 00:00:00 2001
From: Matti R
Date: Mon, 4 Oct 2021 16:26:59 -0400
Subject: [PATCH 2/6] correct documentation
---
cmd/admin/users/list.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmd/admin/users/list.go b/cmd/admin/users/list.go
index c29020c..31cf8a9 100644
--- a/cmd/admin/users/list.go
+++ b/cmd/admin/users/list.go
@@ -31,7 +31,7 @@ var CmdUserList = cli.Command{
}, flags.AllDefaultFlags...),
}
-// RunUserList list user organizations
+// RunUserList list users
func RunUserList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
--
2.40.1
From 3aaac9b0d0f6af3b6876b5aee5f21f070436cb39 Mon Sep 17 00:00:00 2001
From: Norwin
Date: Thu, 17 Mar 2022 10:09:41 +0800
Subject: [PATCH 3/6] Enhance Admin User Listing (#427) (#471)
This is a patch for #427:
- merge `master`
- address my own review
- add some more printable fields
Co-authored-by: kellya
Co-authored-by: Ikko Ashimine
Co-authored-by: arkamar
Co-authored-by: Lunny Xiao
Co-authored-by: techknowlogick
Co-authored-by: Norwin
Reviewed-on: https://gitea.com/gitea/tea/pulls/471
Co-authored-by: Norwin
Co-committed-by: Norwin
---
.drone.yml | 22 ++-
.gitea/issue_template/bug.md | 30 ++++
CONTRIBUTING.md | 10 +-
Makefile | 19 +--
README.md | 45 +++--
build.go | 3 +-
cmd/admin.go | 41 ++++-
cmd/admin/users/list.go | 10 +-
cmd/admin_users.go | 41 -----
cmd/categories.go | 1 -
cmd/clone.go | 89 ++++++++++
cmd/comment.go | 14 +-
cmd/flags/csvflag.go | 8 +-
cmd/flags/{flags.go => generic.go} | 98 -----------
cmd/flags/issue_pr.go | 161 ++++++++++++++++++
cmd/issues.go | 2 +-
cmd/issues/list.go | 51 +++++-
cmd/milestones/create.go | 2 +-
cmd/pulls.go | 2 +-
cmd/pulls/create.go | 6 +-
cmd/pulls/list.go | 2 +-
cmd/releases/create.go | 6 +-
cmd/repos.go | 2 +
cmd/repos/create_from_template.go | 121 +++++++++++++
cmd/repos/fork.go | 57 +++++++
go.mod | 2 -
go.sum | 4 +-
main.go | 12 +-
modules/config/config.go | 14 +-
modules/config/login.go | 19 +++
modules/context/context.go | 5 +
modules/git/url.go | 19 ++-
modules/git/url_test.go | 56 ++++++
modules/interact/comments.go | 1 +
modules/interact/issue_create.go | 7 +-
modules/interact/milestone_create.go | 6 +-
modules/interact/prompts.go | 23 ++-
modules/interact/pull_review.go | 8 +-
modules/print/comment.go | 4 +-
modules/print/formatters.go | 8 +
modules/print/issue.go | 2 +-
modules/print/pull.go | 2 +-
modules/print/table.go | 2 +-
modules/print/user.go | 6 +
modules/task/pull_create.go | 2 +-
modules/task/repo_clone.go | 93 ++++++++++
modules/utils/parse.go | 2 +-
.../charmbracelet/glamour/README.md | 4 +-
.../charmbracelet/glamour/ansi/elements.go | 4 -
.../charmbracelet/glamour/ansi/image.go | 2 +-
.../charmbracelet/glamour/ansi/link.go | 2 +-
.../charmbracelet/glamour/ansi/listitem.go | 3 +-
.../charmbracelet/glamour/ansi/paragraph.go | 2 +-
.../charmbracelet/glamour/ansi/renderer.go | 13 +-
.../charmbracelet/glamour/glamour.go | 8 -
vendor/modules.txt | 2 +-
56 files changed, 907 insertions(+), 273 deletions(-)
create mode 100644 .gitea/issue_template/bug.md
delete mode 100644 cmd/admin_users.go
create mode 100644 cmd/clone.go
rename cmd/flags/{flags.go => generic.go} (57%)
create mode 100644 cmd/flags/issue_pr.go
create mode 100644 cmd/repos/create_from_template.go
create mode 100644 cmd/repos/fork.go
create mode 100644 modules/git/url_test.go
create mode 100644 modules/task/repo_clone.go
diff --git a/.drone.yml b/.drone.yml
index 4d08751..d7c79f2 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -9,7 +9,7 @@ platform:
steps:
- name: build
pull: always
- image: golang:1.16
+ image: golang:1.17
environment:
GOPROXY: https://goproxy.cn
commands:
@@ -27,7 +27,7 @@ steps:
- pull_request
- name: unit-test
- image: golang:1.16
+ image: golang:1.17
commands:
- make unit-test-coverage
settings:
@@ -40,7 +40,7 @@ steps:
- pull_request
- name: release-test
- image: golang:1.16
+ image: golang:1.17
commands:
- make test
settings:
@@ -54,7 +54,7 @@ steps:
- name: tag-test
pull: always
- image: golang:1.16
+ image: golang:1.17
commands:
- make test
settings:
@@ -64,7 +64,7 @@ steps:
- tag
- name: static
- image: golang:1.16
+ image: golang:1.17
environment:
GOPROXY: https://goproxy.cn
commands:
@@ -96,7 +96,7 @@ steps:
- name: tag-release
pull: always
- image: plugins/s3:1
+ image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts
@@ -116,12 +116,11 @@ steps:
- name: release-branch-release
pull: always
- image: plugins/s3:1
+ image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts
- endpoint: https://storage.gitea.io
- path_style: true
+ endpoint: https://ams3.digitaloceanspaces.com
source: "dist/release/*"
strip_prefix: dist/release/
target: "/tea/${DRONE_BRANCH##release/v}"
@@ -138,12 +137,11 @@ steps:
- name: release
pull: always
- image: plugins/s3:1
+ image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts
- endpoint: https://storage.gitea.io
- path_style: true
+ endpoint: https://ams3.digitaloceanspaces.com
source: "dist/release/*"
strip_prefix: dist/release/
target: /tea/master
diff --git a/.gitea/issue_template/bug.md b/.gitea/issue_template/bug.md
new file mode 100644
index 0000000..4dacdd6
--- /dev/null
+++ b/.gitea/issue_template/bug.md
@@ -0,0 +1,30 @@
+---
+name: "Bug Report"
+about: "Use this template when reporting a bug, so you don't forget important information we'd ask for later."
+title: "Bug: "
+labels:
+- kind/bug
+---
+
+### describe your environment
+- tea version used (`tea -v`):
+ - [ ] I also reproduced the issue [with the latest master build](https://dl.gitea.io/tea/master)
+- Gitea version used:
+ - [ ] the issue only occurred after updating gitea recently
+- operating system:
+- I make use of...
+ - [ ] non-standard default branch names (no `main`,`master`, or `trunk`)
+ - [ ] .ssh/config or .gitconfig host aliases in my git remotes
+ - [ ] ssh_agent or similar
+ - [ ] non-standard ports for gitea and/or ssh
+ - [ ] something else that's likely to interact badly with tea: ...
+
+
+Please provide the output of `git remote -v` (if the issue is related to tea not finding resources on Gitea):
+```
+
+```
+
+### describe the issue (observed vs expected behaviour)
+
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d32bfba..498d474 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -57,8 +57,8 @@ high-level discussions.
## Testing redux
-Before sending code out for review, run all the test by execting: `make test`
-Since TEA is an cli tool it should be obvious to test your feature localy first.
+Before sending code out for review, run all the test by executing: `make test`
+Since TEA is an cli tool it should be obvious to test your feature locally first.
## Vendoring
@@ -77,7 +77,7 @@ You can find more information on how to get started with it on the [dep project
## Code review
Changes to TEA must be reviewed before they are accepted—no matter who
-makes the change, even if they are an owner or a maintainer. We use Giteas's
+makes the change, even if they are an owner or a maintainer. We use Gitea's
pull request & review workflow to do that. Gitea ensure every PR is reviewed by at least 2 maintainers.
Please try to make your pull request easy to review for us. And, please read
@@ -118,8 +118,8 @@ Some of the key points:
- Always make sure that the help texts are properly set, and as concise as possible.
### Internal Module Structure
-- `cmd`: only contains comand/flag options for `urfave/cli`
- - subcomands are in a subpackage named after its parent comand
+- `cmd`: only contains command/flag options for `urfave/cli`
+ - subcommands are in a subpackage named after its parent command
- `modules/task`: contain func for doing something with gitea
(e.g. create token by user/passwd)
- `modules/print`: contain all functions that print to stdout
diff --git a/Makefile b/Makefile
index 5a1c971..3b2c956 100644
--- a/Makefile
+++ b/Makefile
@@ -26,24 +26,20 @@ TEA_VERSION_TAG ?= $(shell sed 's/+/_/' <<< $(TEA_VERSION))
TAGS ?=
LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)" -s -w
-ifeq ($(STATIC),true)
- # NOTE: clean up this mess, when https://github.com/golang/go/issues/26492 is resolved
- # static_build is a defacto standard tag used in go packages
- TAGS := osusergo,netgo,static_build,$(TAGS)
- LDFLAGS := $(LDFLAGS) -linkmode=external -extldflags "-static-pie" -X "main.Tags=$(TAGS)"
- export CGO_ENABLED=1 # needed for linkmode=external
-endif
-
# override to allow passing additional goflags via make CLI
override GOFLAGS := $(GOFLAGS) -mod=vendor -tags '$(TAGS)' -ldflags '$(LDFLAGS)'
PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
SOURCES ?= $(shell find . -name "*.go" -type f)
+# OS specific vars.
ifeq ($(OS), Windows_NT)
EXECUTABLE := tea.exe
else
EXECUTABLE := tea
+ ifneq ($(shell uname -s), OpenBSD)
+ override BUILDMODE := -buildmode=pie
+ endif
endif
.PHONY: all
@@ -124,16 +120,13 @@ check: test
.PHONY: install
install: $(SOURCES)
@echo "installing to $(GOPATH)/bin/$(EXECUTABLE)"
- $(GO) install -v -buildmode=pie $(GOFLAGS)
+ $(GO) install -v $(BUILDMODE) $(GOFLAGS)
.PHONY: build
build: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES)
-ifeq ($(STATIC),true)
- @echo "enabling static build, make sure you have glibc-static (or equivalent) installed"
-endif
- $(GO) build -buildmode=pie $(GOFLAGS) -o $@
+ $(GO) build $(BUILDMODE) $(GOFLAGS) -o $@
.PHONY: build-image
build-image:
diff --git a/README.md b/README.md
index 87e9bd9..052f49d 100644
--- a/README.md
+++ b/README.md
@@ -8,16 +8,19 @@
```
tea - command line tool to interact with Gitea
- version 0.7.0-preview
+ version 0.8.0-preview
USAGE
tea command [subcommand] [command options] [arguments...]
DESCRIPTION
- tea is a productivity helper for Gitea. It can be used to manage most entities on one
- or multiple Gitea instances and provides local helpers like 'tea pull checkout'.
- tea makes use of context provided by the repository in $PWD if available, but is still
- usable independently of $PWD. Configuration is persisted in $XDG_CONFIG_HOME/tea.
+ tea is a productivity helper for Gitea. It can be used to manage most entities on
+ one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.
+
+ tea tries to make use of context provided by the repository in $PWD if available.
+ tea works best in a upstream/fork workflow, when the local main branch tracks the
+ upstream repo. tea assumes that local git state is published on the remote before
+ doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.
COMMANDS
help, h Shows a list of commands or help for one command
@@ -30,13 +33,16 @@
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Show repository details
+ comment, c Add a comment to an issue / pr
HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
+ clone, C Clone a repository locally
SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
shellcompletion, autocomplete Install shell completion for tea
+ whoami Show current logged in user
OPTIONS
--help, -h show help (default: false)
@@ -79,29 +85,32 @@ There are different ways to get `tea`:
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
brew install tea
```
- - arch linux ([gitea-tea](https://aur.archlinux.org/packages/gitea-tea), thirdparty)
+ - arch linux ([gitea-tea-git](https://aur.archlinux.org/packages/gitea-tea-git), thirdparty)
- alpine linux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge), thirdparty)
2. Use the prebuilt binaries from [dl.gitea.io](https://dl.gitea.io/tea/)
-3. Install from source (go 1.13 or newer is required):
- ```sh
- go get code.gitea.io/tea
- go install code.gitea.io/tea
- ```
+3. Install from source: [see *Compilation*](#compilation)
4. Docker (thirdparty): [tgerczei/tea](https://hub.docker.com/r/tgerczei/tea)
## Compilation
-Make sure you have installed a current go version.
-To compile the sources yourself run the following:
+Make sure you have a current go version installed (1.13 or newer).
-```sh
-git clone https://gitea.com/gitea/tea.git
-cd tea
-make STATIC=true
-```
+- To compile the source yourself with the recommended flags & tags:
+ ```sh
+ git clone https://gitea.com/gitea/tea.git # or: tea clone gitea.com/gitea/tea ;)
+ cd tea
+ make
+ ```
+ Note that GNU Make (gmake on OpenBSD) is required.
+
+- For a quick installation without `git` & `make`:
+ ```sh
+ go get code.gitea.io/tea
+ go install code.gitea.io/tea
+ ```
## Contributing
diff --git a/build.go b/build.go
index 4a0d72c..0f7e838 100644
--- a/build.go
+++ b/build.go
@@ -1,7 +1,8 @@
// 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.
-//+build vendor
+//go:build vendor
+// +build vendor
package main
diff --git a/cmd/admin.go b/cmd/admin.go
index c8640df..6dacebf 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -5,20 +5,51 @@
package cmd
import (
+ "code.gitea.io/tea/cmd/admin/users"
+ "code.gitea.io/tea/modules/context"
+ "code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v2"
)
-// CmdAdmin represents the admin sub-commands
+// CmdAdmin represents the namespace of admin commands.
+// The command itself has no functionality, but hosts subcommands.
var CmdAdmin = cli.Command{
Name: "admin",
+ Usage: "Operations requiring admin access on the Gitea instance",
Aliases: []string{"a"},
- Category: catAdmin,
+ Category: catHelpers,
Action: func(cmd *cli.Context) error {
- // TODO: this is just a stub for all admin actions
- // there is no default admin action
- return nil
+ return cli.ShowSubcommandHelp(cmd)
},
Subcommands: []*cli.Command{
&cmdAdminUsers,
},
}
+
+var cmdAdminUsers = cli.Command{
+ Name: "users",
+ Aliases: []string{"u"},
+ Usage: "Manage registered users",
+ Action: func(ctx *cli.Context) error {
+ if ctx.Args().Len() == 1 {
+ return runAdminUserDetail(ctx, ctx.Args().First())
+ }
+ return users.RunUserList(ctx)
+ },
+ Subcommands: []*cli.Command{
+ &users.CmdUserList,
+ },
+ Flags: users.CmdUserList.Flags,
+}
+
+func runAdminUserDetail(cmd *cli.Context, u string) error {
+ ctx := context.InitCommand(cmd)
+ client := ctx.Login.Client()
+ user, _, err := client.GetUserInfo(u)
+ if err != nil {
+ return err
+ }
+
+ print.UserDetails(user)
+ return nil
+}
diff --git a/cmd/admin/users/list.go b/cmd/admin/users/list.go
index 31cf8a9..8205aed 100644
--- a/cmd/admin/users/list.go
+++ b/cmd/admin/users/list.go
@@ -34,16 +34,16 @@ var CmdUserList = cli.Command{
// RunUserList list users
func RunUserList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
- client := ctx.Login.Client()
- users, _, err := client.AdminListUsers(gitea.AdminListUsersOptions{
- ListOptions: ctx.GetListOptions(),
- })
+ fields, err := userFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
- fields, err := userFieldsFlag.GetValues(cmd)
+ client := ctx.Login.Client()
+ users, _, err := client.AdminListUsers(gitea.AdminListUsersOptions{
+ ListOptions: ctx.GetListOptions(),
+ })
if err != nil {
return err
}
diff --git a/cmd/admin_users.go b/cmd/admin_users.go
deleted file mode 100644
index 056c996..0000000
--- a/cmd/admin_users.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 cmd
-
-import (
- "code.gitea.io/tea/cmd/admin/users"
- "code.gitea.io/tea/modules/context"
- "code.gitea.io/tea/modules/print"
-
- "github.com/urfave/cli/v2"
-)
-
-var cmdAdminUsers = cli.Command{
- Name: "user",
- Aliases: []string{"u"},
- Category: catAdmin,
- Action: func(ctx *cli.Context) error {
- if ctx.Args().Len() == 1 {
- return runAdminUserDetail(ctx, ctx.Args().First())
- }
- return users.RunUserList(ctx)
- },
- Subcommands: []*cli.Command{
- &users.CmdUserList,
- },
- Flags: users.CmdUserList.Flags,
-}
-
-func runAdminUserDetail(cmd *cli.Context, u string) error {
- ctx := context.InitCommand(cmd)
- client := ctx.Login.Client()
- user, _, err := client.GetUserInfo(u)
- if err != nil {
- return err
- }
-
- print.UserDetails(user)
- return nil
-}
diff --git a/cmd/categories.go b/cmd/categories.go
index c368b5a..4b2088b 100644
--- a/cmd/categories.go
+++ b/cmd/categories.go
@@ -8,5 +8,4 @@ var (
catSetup = "SETUP"
catEntities = "ENTITIES"
catHelpers = "HELPERS"
- catAdmin = "ADMIN"
)
diff --git a/cmd/clone.go b/cmd/clone.go
new file mode 100644
index 0000000..e7e4e42
--- /dev/null
+++ b/cmd/clone.go
@@ -0,0 +1,89 @@
+// 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 cmd
+
+import (
+ "fmt"
+
+ "code.gitea.io/tea/cmd/flags"
+ "code.gitea.io/tea/modules/config"
+ "code.gitea.io/tea/modules/context"
+ "code.gitea.io/tea/modules/git"
+ "code.gitea.io/tea/modules/interact"
+ "code.gitea.io/tea/modules/task"
+ "code.gitea.io/tea/modules/utils"
+
+ "github.com/urfave/cli/v2"
+)
+
+// CmdRepoClone represents a sub command of repos to create a local copy
+var CmdRepoClone = cli.Command{
+ Name: "clone",
+ Aliases: []string{"C"},
+ Usage: "Clone a repository locally",
+ Description: `Clone a repository locally, without a local git installation required.
+The repo slug can be specified in different formats:
+ gitea/tea
+ tea
+ gitea.com/gitea/tea
+ git@gitea.com:gitea/tea
+ https://gitea.com/gitea/tea
+ ssh://gitea.com:22/gitea/tea
+When a host is specified in the repo-slug, it will override the login specified with --login.
+ `,
+ Category: catHelpers,
+ Action: runRepoClone,
+ ArgsUsage: " [target dir]",
+ Flags: []cli.Flag{
+ &cli.IntFlag{
+ Name: "depth",
+ Aliases: []string{"d"},
+ Usage: "num commits to fetch, defaults to all",
+ },
+ &flags.LoginFlag,
+ },
+}
+
+func runRepoClone(cmd *cli.Context) error {
+ ctx := context.InitCommand(cmd)
+
+ args := ctx.Args()
+ if args.Len() < 1 {
+ return cli.ShowCommandHelp(cmd, "clone")
+ }
+ dir := args.Get(1)
+
+ var (
+ login *config.Login = ctx.Login
+ owner string = ctx.Login.User
+ repo string
+ )
+
+ // parse first arg as repo specifier
+ repoSlug := args.Get(0)
+ url, err := git.ParseURL(repoSlug)
+ if err != nil {
+ return err
+ }
+
+ owner, repo = utils.GetOwnerAndRepo(url.Path, login.User)
+ if url.Host != "" {
+ login = config.GetLoginByHost(url.Host)
+ if login == nil {
+ return fmt.Errorf("No login configured matching host '%s', run `tea login add` first", url.Host)
+ }
+ }
+
+ _, err = task.RepoClone(
+ dir,
+ login,
+ owner,
+ repo,
+ interact.PromptPassword,
+ ctx.Int("depth"),
+ )
+
+ return err
+}
diff --git a/cmd/comment.go b/cmd/comment.go
index 169aaa3..67b8e08 100644
--- a/cmd/comment.go
+++ b/cmd/comment.go
@@ -9,13 +9,15 @@ import (
"io/ioutil"
"strings"
- "code.gitea.io/tea/modules/interact"
-
- "code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags"
+ "code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/context"
+ "code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils"
+
+ "code.gitea.io/sdk/gitea"
+ "github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
)
@@ -54,7 +56,11 @@ func runAddComment(cmd *cli.Context) error {
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
}
} else if len(body) == 0 {
- if body, err = interact.PromptMultiline("Content"); err != nil {
+ if err = survey.AskOne(interact.NewMultiline(interact.Multiline{
+ Message: "Comment:",
+ Syntax: "md",
+ UseEditor: config.GetPreferences().Editor,
+ }), &body); err != nil {
return err
}
}
diff --git a/cmd/flags/csvflag.go b/cmd/flags/csvflag.go
index 99f80fb..02def8e 100644
--- a/cmd/flags/csvflag.go
+++ b/cmd/flags/csvflag.go
@@ -21,15 +21,19 @@ type CsvFlag struct {
// NewCsvFlag creates a CsvFlag, while setting its usage string and default values
func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag {
+ var availableDesc string
+ if len(availableValues) != 0 {
+ availableDesc = " Available values:"
+ }
return &CsvFlag{
AvailableFields: availableValues,
StringFlag: cli.StringFlag{
Name: name,
Aliases: aliases,
Value: strings.Join(defaults, ","),
- Usage: fmt.Sprintf(`Comma-separated list of %s. Available values:
+ Usage: fmt.Sprintf(`Comma-separated list of %s.%s
%s
- `, usage, strings.Join(availableValues, ",")),
+ `, usage, availableDesc, strings.Join(availableValues, ",")),
},
}
}
diff --git a/cmd/flags/flags.go b/cmd/flags/generic.go
similarity index 57%
rename from cmd/flags/flags.go
rename to cmd/flags/generic.go
index c3de546..b86ab3c 100644
--- a/cmd/flags/flags.go
+++ b/cmd/flags/generic.go
@@ -5,14 +5,6 @@
package flags
import (
- "fmt"
- "strings"
-
- "code.gitea.io/sdk/gitea"
- "code.gitea.io/tea/modules/context"
- "code.gitea.io/tea/modules/task"
-
- "github.com/araddon/dateparse"
"github.com/urfave/cli/v2"
)
@@ -44,13 +36,6 @@ var OutputFlag = cli.StringFlag{
Usage: "Output format. (csv, simple, table, tsv, yaml)",
}
-// StateFlag provides flag to specify issue/pr state, defaulting to "open"
-var StateFlag = cli.StringFlag{
- Name: "state",
- Usage: "Filter by state (all|open|closed)",
- DefaultText: "open",
-}
-
// PaginationPageFlag provides flag for pagination options
var PaginationPageFlag = cli.StringFlag{
Name: "page",
@@ -93,13 +78,6 @@ var AllDefaultFlags = append([]cli.Flag{
&RemoteFlag,
}, LoginOutputFlags...)
-// IssuePRFlags defines flags that should be available on issue & pr listing flags.
-var IssuePRFlags = append([]cli.Flag{
- &StateFlag,
- &PaginationPageFlag,
- &PaginationLimitFlag,
-}, AllDefaultFlags...)
-
// NotificationFlags defines flags that should be available on notifications.
var NotificationFlags = append([]cli.Flag{
NotificationStateFlag,
@@ -121,82 +99,6 @@ var NotificationStateFlag = NewCsvFlag(
[]string{"unread", "pinned"},
)
-// IssuePREditFlags defines flags for properties of issues and PRs
-var IssuePREditFlags = append([]cli.Flag{
- &cli.StringFlag{
- Name: "title",
- Aliases: []string{"t"},
- },
- &cli.StringFlag{
- Name: "description",
- Aliases: []string{"d"},
- },
- &cli.StringFlag{
- Name: "assignees",
- Aliases: []string{"a"},
- Usage: "Comma-separated list of usernames to assign",
- },
- &cli.StringFlag{
- Name: "labels",
- Aliases: []string{"L"},
- Usage: "Comma-separated list of labels to assign",
- },
- &cli.StringFlag{
- Name: "deadline",
- Aliases: []string{"D"},
- Usage: "Deadline timestamp to assign",
- },
- &cli.StringFlag{
- Name: "milestone",
- Aliases: []string{"m"},
- Usage: "Milestone to assign",
- },
-}, LoginRepoFlags...)
-
-// GetIssuePREditFlags parses all IssuePREditFlags
-func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
- opts := gitea.CreateIssueOption{
- Title: ctx.String("title"),
- Body: ctx.String("description"),
- Assignees: strings.Split(ctx.String("assignees"), ","),
- }
- var err error
-
- date := ctx.String("deadline")
- if date != "" {
- t, err := dateparse.ParseAny(date)
- if err != nil {
- return nil, err
- }
- opts.Deadline = &t
- }
-
- client := ctx.Login.Client()
-
- labelNames := strings.Split(ctx.String("labels"), ",")
- if len(labelNames) != 0 {
- if client == nil {
- client = ctx.Login.Client()
- }
- if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil {
- return nil, err
- }
- }
-
- if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 {
- if client == nil {
- client = ctx.Login.Client()
- }
- ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName)
- if err != nil {
- return nil, fmt.Errorf("Milestone '%s' not found", milestoneName)
- }
- opts.Milestone = ms.ID
- }
-
- return &opts, nil
-}
-
// FieldsFlag generates a flag selecting printable fields.
// To retrieve the value, use f.GetValues()
func FieldsFlag(availableFields, defaultFields []string) *CsvFlag {
diff --git a/cmd/flags/issue_pr.go b/cmd/flags/issue_pr.go
new file mode 100644
index 0000000..7f4a514
--- /dev/null
+++ b/cmd/flags/issue_pr.go
@@ -0,0 +1,161 @@
+// Copyright 2019 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 flags
+
+import (
+ "fmt"
+ "strings"
+
+ "code.gitea.io/sdk/gitea"
+ "code.gitea.io/tea/modules/context"
+ "code.gitea.io/tea/modules/task"
+
+ "github.com/araddon/dateparse"
+ "github.com/urfave/cli/v2"
+)
+
+// StateFlag provides flag to specify issue/pr state, defaulting to "open"
+var StateFlag = cli.StringFlag{
+ Name: "state",
+ Usage: "Filter by state (all|open|closed)",
+ DefaultText: "open",
+}
+
+// MilestoneFilterFlag is a CSV flag used to filter issues by milestones
+var MilestoneFilterFlag = NewCsvFlag(
+ "milestones",
+ "milestones to match issues against",
+ []string{"m"}, nil, nil)
+
+// LabelFilterFlag is a CSV flag used to filter issues by labels
+var LabelFilterFlag = NewCsvFlag(
+ "labels",
+ "labels to match issues against",
+ []string{"L"}, nil, nil)
+
+// PRListingFlags defines flags that should be available on pr listing flags.
+var PRListingFlags = append([]cli.Flag{
+ &StateFlag,
+ &PaginationPageFlag,
+ &PaginationLimitFlag,
+}, AllDefaultFlags...)
+
+// IssueListingFlags defines flags that should be available on issue listing flags.
+var IssueListingFlags = append([]cli.Flag{
+ &StateFlag,
+ &cli.StringFlag{
+ Name: "kind",
+ Aliases: []string{"K"},
+ Usage: "Whether to return `issues`, `pulls`, or `all` (you can use this to apply advanced search filters to PRs)",
+ DefaultText: "issues",
+ },
+ &cli.StringFlag{
+ Name: "keyword",
+ Aliases: []string{"k"},
+ Usage: "Filter by search string",
+ },
+ LabelFilterFlag,
+ MilestoneFilterFlag,
+ &cli.StringFlag{
+ Name: "author",
+ Aliases: []string{"A"},
+ },
+ &cli.StringFlag{
+ Name: "assignee",
+ Aliases: []string{"a"},
+ },
+ &cli.StringFlag{
+ Name: "mentions",
+ Aliases: []string{"M"},
+ },
+ &cli.StringFlag{
+ Name: "from",
+ Aliases: []string{"F"},
+ Usage: "Filter by activity after this date",
+ },
+ &cli.StringFlag{
+ Name: "until",
+ Aliases: []string{"u"},
+ Usage: "Filter by activity before this date",
+ },
+ &PaginationPageFlag,
+ &PaginationLimitFlag,
+}, AllDefaultFlags...)
+
+// IssuePREditFlags defines flags for properties of issues and PRs
+var IssuePREditFlags = append([]cli.Flag{
+ &cli.StringFlag{
+ Name: "title",
+ Aliases: []string{"t"},
+ },
+ &cli.StringFlag{
+ Name: "description",
+ Aliases: []string{"d"},
+ },
+ &cli.StringFlag{
+ Name: "assignees",
+ Aliases: []string{"a"},
+ Usage: "Comma-separated list of usernames to assign",
+ },
+ &cli.StringFlag{
+ Name: "labels",
+ Aliases: []string{"L"},
+ Usage: "Comma-separated list of labels to assign",
+ },
+ &cli.StringFlag{
+ Name: "deadline",
+ Aliases: []string{"D"},
+ Usage: "Deadline timestamp to assign",
+ },
+ &cli.StringFlag{
+ Name: "milestone",
+ Aliases: []string{"m"},
+ Usage: "Milestone to assign",
+ },
+}, LoginRepoFlags...)
+
+// GetIssuePREditFlags parses all IssuePREditFlags
+func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
+ opts := gitea.CreateIssueOption{
+ Title: ctx.String("title"),
+ Body: ctx.String("description"),
+ Assignees: strings.Split(ctx.String("assignees"), ","),
+ }
+ var err error
+
+ date := ctx.String("deadline")
+ if date != "" {
+ t, err := dateparse.ParseAny(date)
+ if err != nil {
+ return nil, err
+ }
+ opts.Deadline = &t
+ }
+
+ client := ctx.Login.Client()
+
+ labelNames := strings.Split(ctx.String("labels"), ",")
+ if len(labelNames) != 0 {
+ if client == nil {
+ client = ctx.Login.Client()
+ }
+ if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil {
+ return nil, err
+ }
+ }
+
+ if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 {
+ if client == nil {
+ client = ctx.Login.Client()
+ }
+ ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName)
+ if err != nil {
+ return nil, fmt.Errorf("Milestone '%s' not found", milestoneName)
+ }
+ opts.Milestone = ms.ID
+ }
+
+ return &opts, nil
+}
diff --git a/cmd/issues.go b/cmd/issues.go
index 658f98d..3648e4f 100644
--- a/cmd/issues.go
+++ b/cmd/issues.go
@@ -34,7 +34,7 @@ var CmdIssues = cli.Command{
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "comments",
- Usage: "Wether to display comments (will prompt if not provided & run interactively)",
+ Usage: "Whether to display comments (will prompt if not provided & run interactively)",
},
}, issues.CmdIssuesList.Flags...),
}
diff --git a/cmd/issues/list.go b/cmd/issues/list.go
index 3089af3..e5999fb 100644
--- a/cmd/issues/list.go
+++ b/cmd/issues/list.go
@@ -5,11 +5,15 @@
package issues
import (
+ "fmt"
+ "time"
+
"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/araddon/dateparse"
"github.com/urfave/cli/v2"
)
@@ -24,7 +28,7 @@ var CmdIssuesList = cli.Command{
Usage: "List issues of the repository",
Description: `List issues of the repository`,
Action: RunIssuesList,
- Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssuePRFlags...),
+ Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssueListingFlags...),
}
// RunIssuesList list issues
@@ -36,16 +40,57 @@ func RunIssuesList(cmd *cli.Context) error {
switch ctx.String("state") {
case "all":
state = gitea.StateAll
- case "open":
+ case "", "open":
state = gitea.StateOpen
case "closed":
state = gitea.StateClosed
+ default:
+ return fmt.Errorf("unknown state '%s'", ctx.String("state"))
}
+ kind := gitea.IssueTypeIssue
+ switch ctx.String("kind") {
+ case "", "issues", "issue":
+ kind = gitea.IssueTypeIssue
+ case "pulls", "pull", "pr":
+ kind = gitea.IssueTypePull
+ case "all":
+ kind = gitea.IssueTypeAll
+ default:
+ return fmt.Errorf("unknown kind '%s'", ctx.String("kind"))
+ }
+
+ var err error
+ var from, until time.Time
+ if ctx.IsSet("from") {
+ from, err = dateparse.ParseLocal(ctx.String("from"))
+ if err != nil {
+ return err
+ }
+ }
+ if ctx.IsSet("until") {
+ until, err = dateparse.ParseLocal(ctx.String("until"))
+ if err != nil {
+ return err
+ }
+ }
+
+ // ignore error, as we don't do any input validation on these flags
+ labels, _ := flags.LabelFilterFlag.GetValues(cmd)
+ milestones, _ := flags.MilestoneFilterFlag.GetValues(cmd)
+
issues, _, err := ctx.Login.Client().ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{
ListOptions: ctx.GetListOptions(),
State: state,
- Type: gitea.IssueTypeIssue,
+ Type: kind,
+ KeyWord: ctx.String("keyword"),
+ CreatedBy: ctx.String("author"),
+ AssignedBy: ctx.String("assigned-to"),
+ MentionedBy: ctx.String("mentions"),
+ Labels: labels,
+ Milestones: milestones,
+ Since: from,
+ Before: until,
})
if err != nil {
diff --git a/cmd/milestones/create.go b/cmd/milestones/create.go
index 0a3001b..e0f41d2 100644
--- a/cmd/milestones/create.go
+++ b/cmd/milestones/create.go
@@ -55,7 +55,7 @@ func runMilestonesCreate(cmd *cli.Context) error {
deadline := &time.Time{}
if date != "" {
t, err := dateparse.ParseAny(date)
- if err == nil {
+ if err != nil {
return err
}
deadline = &t
diff --git a/cmd/pulls.go b/cmd/pulls.go
index 8cb3a85..76dd842 100644
--- a/cmd/pulls.go
+++ b/cmd/pulls.go
@@ -30,7 +30,7 @@ var CmdPulls = cli.Command{
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "comments",
- Usage: "Wether to display comments (will prompt if not provided & run interactively)",
+ Usage: "Whether to display comments (will prompt if not provided & run interactively)",
},
}, pulls.CmdPullsList.Flags...),
Subcommands: []*cli.Command{
diff --git a/cmd/pulls/create.go b/cmd/pulls/create.go
index e1a1d11..bc9de1e 100644
--- a/cmd/pulls/create.go
+++ b/cmd/pulls/create.go
@@ -18,17 +18,17 @@ var CmdPullsCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create a pull-request",
- Description: "Create a pull-request",
+ Description: "Create a pull-request in the current repo",
Action: runPullsCreate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "head",
- Usage: "Set head branch (default is current one)",
+ Usage: "Branch name of the PR source (default is current one). To specify a different head repo, use :",
},
&cli.StringFlag{
Name: "base",
Aliases: []string{"b"},
- Usage: "Set base branch (default is default branch)",
+ Usage: "Branch name of the PR target (default is repos default branch)",
},
}, flags.IssuePREditFlags...),
}
diff --git a/cmd/pulls/list.go b/cmd/pulls/list.go
index 54f3a1a..b09f6bc 100644
--- a/cmd/pulls/list.go
+++ b/cmd/pulls/list.go
@@ -24,7 +24,7 @@ var CmdPullsList = cli.Command{
Usage: "List pull requests of the repository",
Description: `List pull requests of the repository`,
Action: RunPullsList,
- Flags: append([]cli.Flag{pullFieldsFlag}, flags.IssuePRFlags...),
+ Flags: append([]cli.Flag{pullFieldsFlag}, flags.PRListingFlags...),
}
// RunPullsList return list of pulls
diff --git a/cmd/releases/create.go b/cmd/releases/create.go
index ef203f3..3fe3483 100644
--- a/cmd/releases/create.go
+++ b/cmd/releases/create.go
@@ -27,11 +27,11 @@ var CmdReleaseCreate = cli.Command{
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "tag",
- Usage: "Tag name",
+ Usage: "Tag name. If the tag does not exist yet, it will be created by Gitea",
},
&cli.StringFlag{
Name: "target",
- Usage: "Target refs, branch name or commit id",
+ Usage: "Target branch name or commit hash. Defaults to the default branch of the repo",
},
&cli.StringFlag{
Name: "title",
@@ -56,7 +56,7 @@ var CmdReleaseCreate = cli.Command{
&cli.StringSliceFlag{
Name: "asset",
Aliases: []string{"a"},
- Usage: "List of files to attach",
+ Usage: "Path to file attachment. Can be specified multiple times",
},
}, flags.AllDefaultFlags...),
}
diff --git a/cmd/repos.go b/cmd/repos.go
index 3962706..47f0237 100644
--- a/cmd/repos.go
+++ b/cmd/repos.go
@@ -27,6 +27,8 @@ var CmdRepos = cli.Command{
&repos.CmdReposList,
&repos.CmdReposSearch,
&repos.CmdRepoCreate,
+ &repos.CmdRepoCreateFromTemplate,
+ &repos.CmdRepoFork,
},
Flags: repos.CmdReposListFlags,
}
diff --git a/cmd/repos/create_from_template.go b/cmd/repos/create_from_template.go
new file mode 100644
index 0000000..f6511c0
--- /dev/null
+++ b/cmd/repos/create_from_template.go
@@ -0,0 +1,121 @@
+// 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 repos
+
+import (
+ "fmt"
+
+ "code.gitea.io/tea/cmd/flags"
+ "code.gitea.io/tea/modules/context"
+ "code.gitea.io/tea/modules/print"
+ "code.gitea.io/tea/modules/utils"
+
+ "code.gitea.io/sdk/gitea"
+ "github.com/urfave/cli/v2"
+)
+
+// CmdRepoCreateFromTemplate represents a sub command of repos to generate one from a template repo
+var CmdRepoCreateFromTemplate = cli.Command{
+ Name: "create-from-template",
+ Aliases: []string{"ct"},
+ Usage: "Create a repository based on an existing template",
+ Description: "Create a repository based on an existing template",
+ Action: runRepoCreateFromTemplate,
+ Flags: append([]cli.Flag{
+ &cli.StringFlag{
+ Name: "template",
+ Aliases: []string{"t"},
+ Required: true,
+ Usage: "source template to copy from",
+ },
+ &cli.StringFlag{
+ Name: "name",
+ Aliases: []string{"n"},
+ Required: true,
+ Usage: "name of new repo",
+ },
+ &cli.StringFlag{
+ Name: "owner",
+ Aliases: []string{"O"},
+ Usage: "name of repo owner",
+ },
+ &cli.BoolFlag{
+ Name: "private",
+ Usage: "make new repo private",
+ },
+ &cli.StringFlag{
+ Name: "description",
+ Aliases: []string{"desc"},
+ Usage: "add custom description to repo",
+ },
+ &cli.BoolFlag{
+ Name: "content",
+ Value: true,
+ Usage: "copy git content from template",
+ },
+ &cli.BoolFlag{
+ Name: "githooks",
+ Value: true,
+ Usage: "copy git hooks from template",
+ },
+ &cli.BoolFlag{
+ Name: "avatar",
+ Value: true,
+ Usage: "copy repo avatar from template",
+ },
+ &cli.BoolFlag{
+ Name: "labels",
+ Value: true,
+ Usage: "copy repo labels from template",
+ },
+ &cli.BoolFlag{
+ Name: "topics",
+ Value: true,
+ Usage: "copy topics from template",
+ },
+ &cli.BoolFlag{
+ Name: "webhooks",
+ Usage: "copy webhooks from template",
+ },
+ }, flags.LoginOutputFlags...),
+}
+
+func runRepoCreateFromTemplate(cmd *cli.Context) error {
+ ctx := context.InitCommand(cmd)
+ client := ctx.Login.Client()
+
+ templateOwner, templateRepo := utils.GetOwnerAndRepo(ctx.String("template"), ctx.Login.User)
+ owner := ctx.Login.User
+ if ctx.IsSet("owner") {
+ owner = ctx.String("owner")
+ }
+
+ opts := gitea.CreateRepoFromTemplateOption{
+ Name: ctx.String("name"),
+ Owner: owner,
+ Description: ctx.String("description"),
+ Private: ctx.Bool("private"),
+ GitContent: ctx.Bool("content"),
+ GitHooks: ctx.Bool("githooks"),
+ Avatar: ctx.Bool("avatar"),
+ Labels: ctx.Bool("labels"),
+ Topics: ctx.Bool("topics"),
+ Webhooks: ctx.Bool("webhooks"),
+ }
+
+ repo, _, err := client.CreateRepoFromTemplate(templateOwner, templateRepo, opts)
+ if err != nil {
+ return err
+ }
+
+ topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
+ if err != nil {
+ return err
+ }
+ print.RepoDetails(repo, topics)
+
+ fmt.Printf("%s\n", repo.HTMLURL)
+ return nil
+}
diff --git a/cmd/repos/fork.go b/cmd/repos/fork.go
new file mode 100644
index 0000000..1811fce
--- /dev/null
+++ b/cmd/repos/fork.go
@@ -0,0 +1,57 @@
+// 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 repos
+
+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"
+)
+
+// CmdRepoFork represents a sub command of repos to fork an existing repo
+var CmdRepoFork = cli.Command{
+ Name: "fork",
+ Aliases: []string{"f"},
+ Usage: "Fork an existing repository",
+ Description: "Create a repository from an existing repo",
+ Action: runRepoFork,
+ Flags: append([]cli.Flag{
+ &cli.StringFlag{
+ Name: "owner",
+ Aliases: []string{"O"},
+ Usage: "name of fork's owner, defaults to current user",
+ },
+ }, flags.LoginRepoFlags...),
+}
+
+func runRepoFork(cmd *cli.Context) error {
+ ctx := context.InitCommand(cmd)
+ client := ctx.Login.Client()
+
+ opts := gitea.CreateForkOption{}
+ if ctx.IsSet("owner") {
+ owner := ctx.String("owner")
+ opts.Organization = &owner
+ }
+
+ repo, _, err := client.CreateFork(ctx.Owner, ctx.Repo, opts)
+ if err != nil {
+ return err
+ }
+
+ topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
+ if err != nil {
+ return err
+ }
+ print.RepoDetails(repo, topics)
+
+ fmt.Printf("%s\n", repo.HTMLURL)
+ return nil
+}
diff --git a/go.mod b/go.mod
index def58ab..d5ec365 100644
--- a/go.mod
+++ b/go.mod
@@ -37,5 +37,3 @@ require (
golang.org/x/tools v0.1.5 // indirect
gopkg.in/yaml.v2 v2.4.0
)
-
-replace github.com/charmbracelet/glamour => github.com/noerw/glamour v0.3.0-patch
diff --git a/go.sum b/go.sum
index 8b763cd..ddb6b1c 100644
--- a/go.sum
+++ b/go.sum
@@ -39,6 +39,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
+github.com/charmbracelet/glamour v0.3.0 h1:3H+ZrKlSg8s+WU6V7eF2eRVYt8lCueffbi7r2+ffGkc=
+github.com/charmbracelet/glamour v0.3.0/go.mod h1:TzF0koPZhqq0YVBNL100cPHznAAjVj7fksX2RInwjGw=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -129,8 +131,6 @@ github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/f
github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8=
github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/noerw/glamour v0.3.0-patch h1:yc3wdbUIySok6KYeX5BtWnlj+PvP1uYeCeTSwq2rtSw=
-github.com/noerw/glamour v0.3.0-patch/go.mod h1:TzF0koPZhqq0YVBNL100cPHznAAjVj7fksX2RInwjGw=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
diff --git a/main.go b/main.go
index 130e893..ccd83d9 100644
--- a/main.go
+++ b/main.go
@@ -49,6 +49,7 @@ func main() {
&cmd.CmdOpen,
&cmd.CmdNotifications,
+ &cmd.CmdRepoClone,
&cmd.CmdAdmin,
}
@@ -70,10 +71,13 @@ func formatBuiltWith(Tags string) string {
return " built with: " + strings.Replace(Tags, " ", ", ", -1)
}
-var appDescription = `tea is a productivity helper for Gitea. It can be used to manage most entities on one
-or multiple Gitea instances and provides local helpers like 'tea pull checkout'.
-tea makes use of context provided by the repository in $PWD if available, but is still
-usable independently of $PWD. Configuration is persisted in $XDG_CONFIG_HOME/tea.
+var appDescription = `tea is a productivity helper for Gitea. It can be used to manage most entities on
+one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.
+
+tea tries to make use of context provided by the repository in $PWD if available.
+tea works best in a upstream/fork workflow, when the local main branch tracks the
+upstream repo. tea assumes that local git state is published on the remote before
+doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.
`
var helpTemplate = bold(`
diff --git a/modules/config/config.go b/modules/config/config.go
index 74c7b5a..de6f25a 100644
--- a/modules/config/config.go
+++ b/modules/config/config.go
@@ -17,9 +17,16 @@ import (
"gopkg.in/yaml.v2"
)
+// Preferences that are stored in and read from the config file
+type Preferences struct {
+ // Prefer using an external text editor over inline multiline prompts
+ Editor bool `yaml:"editor"`
+}
+
// LocalConfig represents local configurations
type LocalConfig struct {
- Logins []Login `yaml:"logins"`
+ Logins []Login `yaml:"logins"`
+ Prefs Preferences `yaml:"preferences"`
}
var (
@@ -55,6 +62,11 @@ func GetConfigPath() string {
return configFilePath
}
+// GetPreferences returns preferences based on the config file
+func GetPreferences() Preferences {
+ return config.Prefs
+}
+
// loadConfig load config from file
func loadConfig() (err error) {
loadConfigOnce.Do(func() {
diff --git a/modules/config/login.go b/modules/config/login.go
index 47f944c..ddecd2f 100644
--- a/modules/config/login.go
+++ b/modules/config/login.go
@@ -111,6 +111,25 @@ func GetLoginByToken(token string) *Login {
return nil
}
+// GetLoginByHost finds a login by it's server URL
+func GetLoginByHost(host string) *Login {
+ err := loadConfig()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, l := range config.Logins {
+ loginURL, err := url.Parse(l.URL)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if loginURL.Host == host {
+ return &l
+ }
+ }
+ return nil
+}
+
// DeleteLogin delete a login by name from config
func DeleteLogin(name string) error {
var idx = -1
diff --git a/modules/context/context.go b/modules/context/context.go
index e07ff9c..bfdaf64 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -164,6 +164,11 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
remoteValue = remote
}
if len(gitConfig.Remotes) > 1 {
+ // prefer origin if there is multiple remotes
+ _, ok := gitConfig.Remotes["origin"]
+ if ok {
+ remoteValue = "origin"
+ }
// if master branch is present, use it as the default remote
mainBranches := []string{"main", "master", "trunk"}
for _, b := range mainBranches {
diff --git a/modules/git/url.go b/modules/git/url.go
index 5b5a9a5..f76a666 100644
--- a/modules/git/url.go
+++ b/modules/git/url.go
@@ -22,13 +22,18 @@ type URLParser struct {
func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) {
rawURL = strings.TrimSpace(rawURL)
- // convert the weird git ssh url format to a canonical url:
- // git@gitea.com:gitea/tea -> ssh://git@gitea.com/gitea/tea
- if !protocolRe.MatchString(rawURL) &&
- strings.Contains(rawURL, ":") &&
- // not a Windows path
- !strings.Contains(rawURL, "\\") {
- rawURL = "ssh://" + strings.Replace(rawURL, ":", "/", 1)
+ if !protocolRe.MatchString(rawURL) {
+ // convert the weird git ssh url format to a canonical url:
+ // git@gitea.com:gitea/tea -> ssh://git@gitea.com/gitea/tea
+ if strings.Contains(rawURL, ":") &&
+ // not a Windows path
+ !strings.Contains(rawURL, "\\") {
+ rawURL = "ssh://" + strings.Replace(rawURL, ":", "/", 1)
+ } else if !strings.Contains(rawURL, "@") &&
+ strings.Count(rawURL, "/") == 2 {
+ // match cases like gitea.com/gitea/tea
+ rawURL = "https://" + rawURL
+ }
}
u, err = url.Parse(rawURL)
diff --git a/modules/git/url_test.go b/modules/git/url_test.go
new file mode 100644
index 0000000..f342525
--- /dev/null
+++ b/modules/git/url_test.go
@@ -0,0 +1,56 @@
+// Copyright 2019 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 git
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestParseUrl(t *testing.T) {
+ u, err := ParseURL("ssh://git@gitea.com:3000/gitea/tea")
+ assert.NoError(t, err)
+ assert.Equal(t, "gitea.com:3000", u.Host)
+ assert.Equal(t, "ssh", u.Scheme)
+ assert.Equal(t, "/gitea/tea", u.Path)
+
+ u, err = ParseURL("https://gitea.com/gitea/tea")
+ assert.NoError(t, err)
+ assert.Equal(t, "gitea.com", u.Host)
+ assert.Equal(t, "https", u.Scheme)
+ assert.Equal(t, "/gitea/tea", u.Path)
+
+ u, err = ParseURL("git@gitea.com:gitea/tea")
+ assert.NoError(t, err)
+ assert.Equal(t, "gitea.com", u.Host)
+ assert.Equal(t, "ssh", u.Scheme)
+ assert.Equal(t, "/gitea/tea", u.Path)
+
+ u, err = ParseURL("gitea.com/gitea/tea")
+ assert.NoError(t, err)
+ assert.Equal(t, "gitea.com", u.Host)
+ assert.Equal(t, "https", u.Scheme)
+ assert.Equal(t, "/gitea/tea", u.Path)
+
+ u, err = ParseURL("foo/bar")
+ assert.NoError(t, err)
+ assert.Equal(t, "", u.Host)
+ assert.Equal(t, "", u.Scheme)
+ assert.Equal(t, "foo/bar", u.Path)
+
+ u, err = ParseURL("/foo/bar")
+ assert.NoError(t, err)
+ assert.Equal(t, "", u.Host)
+ assert.Equal(t, "https", u.Scheme)
+ assert.Equal(t, "/foo/bar", u.Path)
+
+ // this case is unintuitive, but to ambiguous to be handled differently
+ u, err = ParseURL("gitea.com")
+ assert.NoError(t, err)
+ assert.Equal(t, "", u.Host)
+ assert.Equal(t, "", u.Scheme)
+ assert.Equal(t, "gitea.com", u.Path)
+}
diff --git a/modules/interact/comments.go b/modules/interact/comments.go
index aebe4bd..a1adcef 100644
--- a/modules/interact/comments.go
+++ b/modules/interact/comments.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
+
"github.com/AlecAivazis/survey/v2"
"golang.org/x/crypto/ssh/terminal"
)
diff --git a/modules/interact/issue_create.go b/modules/interact/issue_create.go
index e9b79a8..4cdab29 100644
--- a/modules/interact/issue_create.go
+++ b/modules/interact/issue_create.go
@@ -43,7 +43,12 @@ func promptIssueProperties(login *config.Login, owner, repo string, o *gitea.Cre
}
// description
- promptD := &survey.Multiline{Message: "Issue description:", Default: o.Body}
+ promptD := NewMultiline(Multiline{
+ Message: "Issue description:",
+ Default: o.Body,
+ Syntax: "md",
+ UseEditor: config.GetPreferences().Editor,
+ })
if err = survey.AskOne(promptD, &o.Body); err != nil {
return err
}
diff --git a/modules/interact/milestone_create.go b/modules/interact/milestone_create.go
index 8e8b2b1..07846b5 100644
--- a/modules/interact/milestone_create.go
+++ b/modules/interact/milestone_create.go
@@ -33,7 +33,11 @@ func CreateMilestone(login *config.Login, owner, repo string) error {
}
// description
- promptM := &survey.Multiline{Message: "Milestone description:"}
+ promptM := NewMultiline(Multiline{
+ Message: "Milestone description:",
+ Syntax: "md",
+ UseEditor: config.GetPreferences().Editor,
+ })
if err := survey.AskOne(promptM, &description); err != nil {
return err
}
diff --git a/modules/interact/prompts.go b/modules/interact/prompts.go
index debe944..4775f39 100644
--- a/modules/interact/prompts.go
+++ b/modules/interact/prompts.go
@@ -14,9 +14,26 @@ import (
"github.com/araddon/dateparse"
)
-// PromptMultiline runs a textfield-style prompt and blocks until input was made.
-func PromptMultiline(message string) (content string, err error) {
- err = survey.AskOne(&survey.Multiline{Message: message}, &content)
+// Multiline represents options for a prompt that expects multiline input
+type Multiline struct {
+ Message string
+ Default string
+ Syntax string
+ UseEditor bool
+}
+
+// NewMultiline creates a prompt that switches between the inline multiline text
+// and a texteditor based prompt
+func NewMultiline(opts Multiline) (prompt survey.Prompt) {
+ if opts.UseEditor {
+ prompt = &survey.Editor{
+ Message: opts.Message,
+ Default: opts.Default,
+ FileName: "*." + opts.Syntax,
+ }
+ } else {
+ prompt = &survey.Multiline{Message: opts.Message, Default: opts.Default}
+ }
return
}
diff --git a/modules/interact/pull_review.go b/modules/interact/pull_review.go
index ea766d7..3b0affd 100644
--- a/modules/interact/pull_review.go
+++ b/modules/interact/pull_review.go
@@ -8,6 +8,7 @@ import (
"fmt"
"os"
+ "code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/task"
@@ -55,7 +56,11 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error {
if (state == gitea.ReviewStateComment && len(codeComments) == 0) || state == gitea.ReviewStateRequestChanges {
promptOpts = survey.WithValidator(survey.Required)
}
- err = survey.AskOne(&survey.Multiline{Message: "Concluding comment:"}, &comment, promptOpts)
+ err = survey.AskOne(NewMultiline(Multiline{
+ Message: "Concluding comment:",
+ Syntax: "md",
+ UseEditor: config.GetPreferences().Editor,
+ }), &comment, promptOpts)
if err != nil {
return err
}
@@ -65,6 +70,7 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error {
// DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $VISUAL or $EDITOR to comment on diff,
// (3) parses resulting file into code comments.
+// It doesn't really make sense to use survey.Editor() here, as we'd read the file content at least twice.
func DoDiffReview(ctx *context.TeaContext, idx int64) ([]gitea.CreatePullReviewComment, error) {
tmpFile, err := task.SavePullDiff(ctx, idx)
if err != nil {
diff --git a/modules/print/comment.go b/modules/print/comment.go
index 14f2764..3fbe5f1 100644
--- a/modules/print/comment.go
+++ b/modules/print/comment.go
@@ -15,7 +15,7 @@ import (
func Comments(comments []*gitea.Comment) {
var baseURL string
if len(comments) != 0 {
- baseURL = comments[0].HTMLURL
+ baseURL = getRepoURL(comments[0].HTMLURL)
}
var out = make([]string, len(comments))
@@ -32,7 +32,7 @@ func Comments(comments []*gitea.Comment) {
// Comment renders a comment to stdout
func Comment(c *gitea.Comment) {
- outputMarkdown(formatComment(c), c.HTMLURL)
+ outputMarkdown(formatComment(c), getRepoURL(c.HTMLURL))
}
func formatComment(c *gitea.Comment) string {
diff --git a/modules/print/formatters.go b/modules/print/formatters.go
index af7a899..fc33151 100644
--- a/modules/print/formatters.go
+++ b/modules/print/formatters.go
@@ -6,12 +6,20 @@ package print
import (
"fmt"
+ "regexp"
"time"
"code.gitea.io/sdk/gitea"
"github.com/muesli/termenv"
)
+// captures the repo URL part // of an url
+var repoURLRegex = regexp.MustCompile("^([[:alnum:]]+://[^/]+(?:/[[:alnum:]]+){2})/.*")
+
+func getRepoURL(resourceURL string) string {
+ return repoURLRegex.ReplaceAllString(resourceURL, "$1/")
+}
+
// formatSize get kb in int and return string
func formatSize(kb int64) string {
if kb < 1024 {
diff --git a/modules/print/issue.go b/modules/print/issue.go
index b78e7be..8f2fb5f 100644
--- a/modules/print/issue.go
+++ b/modules/print/issue.go
@@ -28,7 +28,7 @@ func IssueDetails(issue *gitea.Issue, reactions []*gitea.Reaction) {
out += fmt.Sprintf("\n---\n\n%s\n", formatReactions(reactions))
}
- outputMarkdown(out, issue.HTMLURL)
+ outputMarkdown(out, getRepoURL(issue.HTMLURL))
}
func formatReactions(reactions []*gitea.Reaction) string {
diff --git a/modules/print/pull.go b/modules/print/pull.go
index 5007bde..2bc14b5 100644
--- a/modules/print/pull.go
+++ b/modules/print/pull.go
@@ -64,7 +64,7 @@ func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *g
}
}
- outputMarkdown(out, pr.HTMLURL)
+ outputMarkdown(out, getRepoURL(pr.HTMLURL))
}
func formatPRHead(pr *gitea.PullRequest) string {
diff --git a/modules/print/table.go b/modules/print/table.go
index 7f6988b..26f49f0 100644
--- a/modules/print/table.go
+++ b/modules/print/table.go
@@ -140,7 +140,7 @@ func outputyaml(headers []string, values [][]string) {
func isMachineReadable(outputFormat string) bool {
switch outputFormat {
- case "yml", "yaml", "csv":
+ case "yml", "yaml", "csv", "tsv":
return true
}
return false
diff --git a/modules/print/user.go b/modules/print/user.go
index 21ef43b..34efd3e 100644
--- a/modules/print/user.go
+++ b/modules/print/user.go
@@ -78,6 +78,8 @@ var UserFields = []string{
"description",
"visibility",
"activated",
+ "lastlogin_at",
+ "created_at",
}
type printableUser struct{ *gitea.User }
@@ -124,6 +126,10 @@ func (x printableUser) FormatField(field string, machineReadable bool) string {
return x.Description
case "visibility":
return string(x.Visibility)
+ case "created_at":
+ return FormatTime(x.Created)
+ case "lastlogin_at":
+ return FormatTime(x.LastLogin)
}
return ""
}
diff --git a/modules/task/pull_create.go b/modules/task/pull_create.go
index b7505b5..8ed6ec9 100644
--- a/modules/task/pull_create.go
+++ b/modules/task/pull_create.go
@@ -108,7 +108,7 @@ func GetDefaultPRHead(localRepo *local_git.TeaRepo) (owner, branch string, err e
if err != nil {
return
}
- owner, _ = utils.GetOwnerAndRepo(strings.TrimLeft(url.Path, "/"), "")
+ owner, _ = utils.GetOwnerAndRepo(url.Path, "")
return
}
diff --git a/modules/task/repo_clone.go b/modules/task/repo_clone.go
new file mode 100644
index 0000000..bc164fc
--- /dev/null
+++ b/modules/task/repo_clone.go
@@ -0,0 +1,93 @@
+// 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 task
+
+import (
+ "fmt"
+ "net/url"
+
+ "code.gitea.io/sdk/gitea"
+ "code.gitea.io/tea/modules/config"
+ local_git "code.gitea.io/tea/modules/git"
+
+ "github.com/go-git/go-git/v5"
+ git_config "github.com/go-git/go-git/v5/config"
+ "github.com/go-git/go-git/v5/plumbing"
+)
+
+// RepoClone creates a local git clone in the given path, and sets up upstream remote
+// for fork repos, for good usability with tea.
+func RepoClone(
+ path string,
+ login *config.Login,
+ repoOwner, repoName string,
+ callback func(string) (string, error),
+ depth int,
+) (*local_git.TeaRepo, error) {
+
+ repoMeta, _, err := login.Client().GetRepo(repoOwner, repoName)
+ if err != nil {
+ return nil, err
+ }
+
+ originURL, err := cloneURL(repoMeta, login)
+ if err != nil {
+ return nil, err
+ }
+
+ auth, err := local_git.GetAuthForURL(originURL, login.Token, login.SSHKey, callback)
+ if err != nil {
+ return nil, err
+ }
+
+ // default path behaviour as native git
+ if path == "" {
+ path = repoName
+ }
+
+ repo, err := git.PlainClone(path, false, &git.CloneOptions{
+ URL: originURL.String(),
+ Auth: auth,
+ Depth: depth,
+ InsecureSkipTLS: login.Insecure,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // set up upstream remote for forks
+ if repoMeta.Fork && repoMeta.Parent != nil {
+ upstreamURL, err := cloneURL(repoMeta.Parent, login)
+ if err != nil {
+ return nil, err
+ }
+ upstreamBranch := repoMeta.Parent.DefaultBranch
+ repo.CreateRemote(&git_config.RemoteConfig{
+ Name: "upstream",
+ URLs: []string{upstreamURL.String()},
+ })
+ repoConf, err := repo.Config()
+ if err != nil {
+ return nil, err
+ }
+ if b, ok := repoConf.Branches[upstreamBranch]; ok {
+ b.Remote = "upstream"
+ b.Merge = plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", upstreamBranch))
+ }
+ if err = repo.SetConfig(repoConf); err != nil {
+ return nil, err
+ }
+ }
+
+ return &local_git.TeaRepo{Repository: repo}, nil
+}
+
+func cloneURL(repo *gitea.Repository, login *config.Login) (*url.URL, error) {
+ urlStr := repo.CloneURL
+ if login.SSHKey != "" {
+ urlStr = repo.SSHURL
+ }
+ return local_git.ParseURL(urlStr)
+}
diff --git a/modules/utils/parse.go b/modules/utils/parse.go
index 41e88ae..63fd19e 100644
--- a/modules/utils/parse.go
+++ b/modules/utils/parse.go
@@ -33,7 +33,7 @@ func GetOwnerAndRepo(repoPath, user string) (string, string) {
if len(repoPath) == 0 {
return "", ""
}
- p := strings.Split(repoPath, "/")
+ p := strings.Split(strings.TrimLeft(repoPath, "/"), "/")
if len(p) >= 2 {
return p[0], p[1]
}
diff --git a/vendor/github.com/charmbracelet/glamour/README.md b/vendor/github.com/charmbracelet/glamour/README.md
index 648ab9d..c7c3cec 100644
--- a/vendor/github.com/charmbracelet/glamour/README.md
+++ b/vendor/github.com/charmbracelet/glamour/README.md
@@ -6,10 +6,10 @@
-
+
-Stylesheet-based markdown rendering for your CLI apps.
+Write handsome command-line tools with *Glamour*.
![Glamour dark style example](https://stuff.charm.sh/glamour/glamour-example.png)
diff --git a/vendor/github.com/charmbracelet/glamour/ansi/elements.go b/vendor/github.com/charmbracelet/glamour/ansi/elements.go
index cd253cf..c09ed7f 100644
--- a/vendor/github.com/charmbracelet/glamour/ansi/elements.go
+++ b/vendor/github.com/charmbracelet/glamour/ansi/elements.go
@@ -129,9 +129,6 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element {
}
if node.Parent().(*ast.List).IsOrdered() {
e = l
- if node.Parent().(*ast.List).Start != 1 {
- e += uint(node.Parent().(*ast.List).Start) - 1
- }
}
post := "\n"
@@ -156,7 +153,6 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element {
return Element{
Exiting: post,
Renderer: &ItemElement{
- IsOrdered: node.Parent().(*ast.List).IsOrdered(),
Enumeration: e,
},
}
diff --git a/vendor/github.com/charmbracelet/glamour/ansi/image.go b/vendor/github.com/charmbracelet/glamour/ansi/image.go
index 9f31c4d..f5edfaf 100644
--- a/vendor/github.com/charmbracelet/glamour/ansi/image.go
+++ b/vendor/github.com/charmbracelet/glamour/ansi/image.go
@@ -25,7 +25,7 @@ func (e *ImageElement) Render(w io.Writer, ctx RenderContext) error {
}
if len(e.URL) > 0 {
el := &BaseElement{
- Token: resolveURL(e.BaseURL, e.URL),
+ Token: resolveRelativeURL(e.BaseURL, e.URL),
Prefix: " ",
Style: ctx.options.Styles.Image,
}
diff --git a/vendor/github.com/charmbracelet/glamour/ansi/link.go b/vendor/github.com/charmbracelet/glamour/ansi/link.go
index 3f0dbba..4cb5931 100644
--- a/vendor/github.com/charmbracelet/glamour/ansi/link.go
+++ b/vendor/github.com/charmbracelet/glamour/ansi/link.go
@@ -64,7 +64,7 @@ func (e *LinkElement) Render(w io.Writer, ctx RenderContext) error {
}
el := &BaseElement{
- Token: resolveURL(e.BaseURL, e.URL),
+ Token: resolveRelativeURL(e.BaseURL, e.URL),
Prefix: pre,
Style: style,
}
diff --git a/vendor/github.com/charmbracelet/glamour/ansi/listitem.go b/vendor/github.com/charmbracelet/glamour/ansi/listitem.go
index 4e47af8..a64b10d 100644
--- a/vendor/github.com/charmbracelet/glamour/ansi/listitem.go
+++ b/vendor/github.com/charmbracelet/glamour/ansi/listitem.go
@@ -7,13 +7,12 @@ import (
// An ItemElement is used to render items inside a list.
type ItemElement struct {
- IsOrdered bool
Enumeration uint
}
func (e *ItemElement) Render(w io.Writer, ctx RenderContext) error {
var el *BaseElement
- if e.IsOrdered {
+ if e.Enumeration > 0 {
el = &BaseElement{
Style: ctx.options.Styles.Enumeration,
Prefix: strconv.FormatInt(int64(e.Enumeration), 10),
diff --git a/vendor/github.com/charmbracelet/glamour/ansi/paragraph.go b/vendor/github.com/charmbracelet/glamour/ansi/paragraph.go
index 0d3f99a..71e0725 100644
--- a/vendor/github.com/charmbracelet/glamour/ansi/paragraph.go
+++ b/vendor/github.com/charmbracelet/glamour/ansi/paragraph.go
@@ -38,7 +38,7 @@ func (e *ParagraphElement) Finish(w io.Writer, ctx RenderContext) error {
mw := NewMarginWriter(ctx, w, rules)
if len(strings.TrimSpace(bs.Current().Block.String())) > 0 {
flow := wordwrap.NewWriter(int(bs.Width(ctx)))
- flow.KeepNewlines = ctx.options.PreserveNewLines
+ flow.KeepNewlines = false
_, _ = flow.Write(bs.Current().Block.Bytes())
flow.Close()
diff --git a/vendor/github.com/charmbracelet/glamour/ansi/renderer.go b/vendor/github.com/charmbracelet/glamour/ansi/renderer.go
index 8275694..ddadbb7 100644
--- a/vendor/github.com/charmbracelet/glamour/ansi/renderer.go
+++ b/vendor/github.com/charmbracelet/glamour/ansi/renderer.go
@@ -3,6 +3,7 @@ package ansi
import (
"io"
"net/url"
+ "strings"
"github.com/muesli/termenv"
east "github.com/yuin/goldmark-emoji/ast"
@@ -14,11 +15,10 @@ import (
// Options is used to configure an ANSIRenderer.
type Options struct {
- BaseURL string
- WordWrap int
- PreserveNewLines bool
- ColorProfile termenv.Profile
- Styles StyleConfig
+ BaseURL string
+ WordWrap int
+ ColorProfile termenv.Profile
+ Styles StyleConfig
}
// ANSIRenderer renders markdown content as ANSI escaped sequences.
@@ -149,7 +149,7 @@ func isChild(node ast.Node) bool {
return false
}
-func resolveURL(baseURL string, rel string) string {
+func resolveRelativeURL(baseURL string, rel string) string {
u, err := url.Parse(rel)
if err != nil {
return rel
@@ -157,6 +157,7 @@ func resolveURL(baseURL string, rel string) string {
if u.IsAbs() {
return rel
}
+ u.Path = strings.TrimPrefix(u.Path, "/")
base, err := url.Parse(baseURL)
if err != nil {
diff --git a/vendor/github.com/charmbracelet/glamour/glamour.go b/vendor/github.com/charmbracelet/glamour/glamour.go
index 048d45a..7b4a14c 100644
--- a/vendor/github.com/charmbracelet/glamour/glamour.go
+++ b/vendor/github.com/charmbracelet/glamour/glamour.go
@@ -185,14 +185,6 @@ func WithWordWrap(wordWrap int) TermRendererOption {
}
}
-// WithWordWrap sets a TermRenderer's word wrap.
-func WithPreservedNewLines() TermRendererOption {
- return func(tr *TermRenderer) error {
- tr.ansiOptions.PreserveNewLines = true
- return nil
- }
-}
-
// WithEmoji sets a TermRenderer's emoji rendering.
func WithEmoji() TermRendererOption {
return func(tr *TermRenderer) error {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 325dd7a..2a4d913 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -74,7 +74,7 @@ github.com/araddon/dateparse
# github.com/aymerick/douceur v0.2.0
github.com/aymerick/douceur/css
github.com/aymerick/douceur/parser
-# github.com/charmbracelet/glamour v0.3.0 => github.com/noerw/glamour v0.3.0-patch
+# github.com/charmbracelet/glamour v0.3.0
github.com/charmbracelet/glamour
github.com/charmbracelet/glamour/ansi
# github.com/cpuguy83/go-md2man/v2 v2.0.1
--
2.40.1
From 93c0d3cbc22d5f88435ced28a94d54208e930482 Mon Sep 17 00:00:00 2001
From: Norwin
Date: Tue, 13 Sep 2022 21:24:05 +0200
Subject: [PATCH 5/6] fix merge error
---
modules/print/user.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/print/user.go b/modules/print/user.go
index 34efd3e..52380d3 100644
--- a/modules/print/user.go
+++ b/modules/print/user.go
@@ -127,9 +127,9 @@ func (x printableUser) FormatField(field string, machineReadable bool) string {
case "visibility":
return string(x.Visibility)
case "created_at":
- return FormatTime(x.Created)
+ return FormatTime(x.Created, machineReadable)
case "lastlogin_at":
- return FormatTime(x.LastLogin)
+ return FormatTime(x.LastLogin, machineReadable)
}
return ""
}
--
2.40.1
From d1efad5660d8422036ee5fe1b000f3d7d509f4e2 Mon Sep 17 00:00:00 2001
From: Norwin
Date: Tue, 13 Sep 2022 21:27:31 +0200
Subject: [PATCH 6/6] improve cmd categories
---
cmd/admin.go | 2 +-
cmd/categories.go | 1 +
cmd/whoami.go | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/cmd/admin.go b/cmd/admin.go
index 6dacebf..b20ead3 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -17,7 +17,7 @@ var CmdAdmin = cli.Command{
Name: "admin",
Usage: "Operations requiring admin access on the Gitea instance",
Aliases: []string{"a"},
- Category: catHelpers,
+ Category: catMisc,
Action: func(cmd *cli.Context) error {
return cli.ShowSubcommandHelp(cmd)
},
diff --git a/cmd/categories.go b/cmd/categories.go
index 4b2088b..f3bb3d5 100644
--- a/cmd/categories.go
+++ b/cmd/categories.go
@@ -8,4 +8,5 @@ var (
catSetup = "SETUP"
catEntities = "ENTITIES"
catHelpers = "HELPERS"
+ catMisc = "MISCELLANEOUS"
)
diff --git a/cmd/whoami.go b/cmd/whoami.go
index 2b56828..81d7713 100644
--- a/cmd/whoami.go
+++ b/cmd/whoami.go
@@ -14,7 +14,7 @@ import (
// CmdWhoami represents the command to show current logged in user
var CmdWhoami = cli.Command{
Name: "whoami",
- Category: catSetup,
+ Category: catMisc,
Description: `For debugging purposes, show the user that is currently logged in.`,
Usage: "Show current logged in user",
ArgsUsage: " ", // command does not accept arguments
--
2.40.1