25 Commits

Author SHA1 Message Date
1c690c5ff8 Changelog v0.8.0 (#404)
Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/404
Reviewed-by: Alexey 〒erentyev <axifive@noreply.gitea.io>
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Co-authored-by: 6543 <6543@obermui.de>
Co-committed-by: 6543 <6543@obermui.de>
2021-09-23 03:53:33 +08:00
802bdf7dc5 Don't require a body for comment PR reviews (#399)
fixes #372

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/399
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-23 02:18:25 +08:00
1731e00ebd Don't skip reading the local repo when --repo specifies a repo slug (#398)
I added this check in #327, but it wasn't needed at all
as the error case it intended to catch where already handled by checking if the path exists.

fixes #378

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/398
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-23 00:12:56 +08:00
7b7c7f57be tea pr create: make local repo optional (#393)
this is a partial fix to #378, making the command available outside of a local repo.

new behaviour:
- when run interactively without local repo context, the head repo prompt is not pre-populated
- when run with flags without local repo context, it will complain unless `--head` is specified

refactor:
- pass TeaContext down to task.CreatePull

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/393
Reviewed-by: Alexey 〒erentyev <axifive@noreply.gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-22 23:48:21 +08:00
6e728cf812 Accept more main branch names for login detection (#396)
Also consider `main` and `trunk` as options
to determine a login through its configured remote

fixes #381

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/396
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Alexey 〒erentyev <axifive@noreply.gitea.io>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-14 14:59:11 +08:00
808e8b1c5a Correctly match login by ssh host with port (#391)
fixes #380

note: It seems like it was expected that `SSHHost` only contains the host portion.  So this may be breaking (although I don't believe many people used the feature like that with a custom ssh port). I can't think of a good reason to *not* specify the port in that field, including the port seems more intuitive

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/391
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-06 18:52:34 +08:00
9201250f74 fix printing issue deadline (#388)
fixes #387

Reviewed-on: https://gitea.com/gitea/tea/pulls/388
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-06 18:45:24 +08:00
5b28a05eb7 Implement notification subcommands (#389)
- [x] enhance notification listing
  - add `--states` and `--type` filters
  - toggle per-user or per-repo listing via `--mine` flag
  - print more fields
- [x] add subcommands to mark notifications as read, unread, pinned, unpinned. operates on
  - all notifications matching the `--state` and `--mine` filter flags, or
  - a notification specified by ID.
- [ ] ~~add a `--fields` flag for notifications listing.~~ *not in this PR*
- [ ] ~~interactive mode~~ *not in this PR*. this would go well together with #324

fixes #243, fixes #155

based on initial work in #283 and #386, but opening a new PR for @6543 to review as I changed quite a lot

---

### ⚠️ breaking ⚠️
- `tea notifications --all` has moved to `tea notifications --mine`
- `tea notifications` now only works with the context of a remote repo.
  To run this outside of a local git dir, run either `tea n --mine` or `tea n --repo <my/repo>`

---

Co-authored-by: Karl Heinz Marbaise <kama@soebes.de>
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin Roosen <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/389
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Alexey 〒erentyev <axifive@noreply.gitea.io>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-06 01:11:17 +08:00
3fca309f2c Fix adding login without token on private instances (#392)
fixes #365

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/392
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-08-30 23:19:45 +08:00
d6df0a53b5 Update Dependencies (#390)
Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/390
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-08-30 23:18:50 +08:00
4b9907fb54 Notifications Add State Field (#384)
Reviewed-on: https://gitea.com/gitea/tea/pulls/384
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Co-authored-by: 6543 <6543@obermui.de>
Co-committed-by: 6543 <6543@obermui.de>
2021-08-16 21:23:16 +08:00
ab4e11ae4d Update gitea go-sdk to v0.15.0 (#385)
Update "code.gitea.io/sdk/gitea" to latest release

Reviewed-on: https://gitea.com/gitea/tea/pulls/385
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: 6543 <6543@obermui.de>
Co-committed-by: 6543 <6543@obermui.de>
2021-08-16 20:46:15 +08:00
546fcc16de Handle XDG directories with spaces in autocomplete commands (#383)
On some OSs ([i.e. macOS](https://github.com/adrg/xdg#xdg-base-directory)), XDG directories contain spaces, so we need to wrap the resulting path used in autocomplete sourcing commands in quotation marks.

Co-authored-by: Matthew Daley <mattd@bugfuzz.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/383
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Co-authored-by: nukuta <nukuta@noreply.gitea.io>
Co-committed-by: nukuta <nukuta@noreply.gitea.io>
2021-07-24 00:14:23 +08:00
0f4f669cf0 Fix parsing of --description for issue/pr create (#371)
The `--description` flag didn't work; regression of #331

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/371
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: KN4CK3R <kn4ck3r@noreply.gitea.io>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-07-03 23:23:38 +08:00
2bdd72dfff Improve error messages (#370)
fixes #367

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/370
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: KN4CK3R <kn4ck3r@noreply.gitea.io>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-07-03 22:39:05 +08:00
64770a771f rename s3 bucket name (#375)
Reviewed-on: https://gitea.com/gitea/tea/pulls/375
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-committed-by: techknowlogick <techknowlogick@gitea.io>
2021-06-30 04:36:05 +08:00
ebb2c38a0a Return useful error on wrong sshkey path (#374)
close #366

Reviewed-on: https://gitea.com/gitea/tea/pulls/374
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Co-authored-by: 6543 <6543@obermui.de>
Co-committed-by: 6543 <6543@obermui.de>
2021-06-29 15:54:43 +08:00
616127cedc Add missing flags (#369)
fixes #368

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/369
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-06-22 22:23:09 +08:00
3129e60a73 text editor selection: follow unix defacto standards (#356)
Currently, `tea` only supports the $EDITOR env var to open the user's preferred editor (used for reviewing pull requests).

Standard \*nix practice is, however, to check for $VISUAL first and only then use $EDITOR as fallback.
This is also done by Git itself, see man git-var(1).
(Actually, the order there is $GIT_EDITOR > core.editor > $VISUAL > $EDITOR > vi)

Co-authored-by: plgruener <pl.gruener@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/356
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: plgruener <plgruener@noreply.gitea.io>
Co-committed-by: plgruener <plgruener@noreply.gitea.io>
2021-06-21 20:08:27 +08:00
df724b4006 Add tab completion for fish shell (#364)
as title, fixes #361

Handling of fish shell is different in urfave/cli; urfave/cli provides a generator for the shell script needed (probably because the fish `completion` syntax isn't flexible enough to let the application handle the completion at runtime? idk)

This means that the fish completion can become out of sync with the tea binary.
If we want to account for that, on each application run we need to
- check if `~/.config/fish/conf.d/tea_completion.fish` exists; if so
- check if the tea version that wrote it is the currently running version
- if not, rewrite the file.

Not sure this is worth the complexity & cost

It generates a completion that also suggests file names, which looks kinda messy: Didn't find a way around this, but [there may be a way](5bb54ace57/fish.go (L160-L180))
![grafik](/attachments/b08541c9-0f37-4c70-a2e3-1ec9da15a430)

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/364
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-05-24 04:42:21 +08:00
ffdbdb3d02 Check negative limit command parameter (#358) (#359)
fix #358

Co-authored-by: Brahim Hamdouni <brahim@hamdouni.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/359
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Co-authored-by: Brahim HAMDOUNI <hamdouni@noreply.gitea.io>
Co-committed-by: Brahim HAMDOUNI <hamdouni@noreply.gitea.io>
2021-05-15 22:16:24 +08:00
568fde1ce5 Add missing flags to org & labels subcommands (#357)
The `tea orgs` command is an alias to `tea orgs list`, and as such should have the same flags.

fixes #354

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/357
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-05-13 02:32:20 +08:00
46945415c9 Enable release builds for darwin/arm64 (#360)
Co-authored-by: Norwin Roosen <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/360
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-05-11 08:03:39 +08:00
195bd2199c contributed container build definition (#350)
as discussed with @noerw on Discord

Co-authored-by: Tamás Gérczei <tamas@gerczei.eu>
Reviewed-on: https://gitea.com/gitea/tea/pulls/350
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Tamás Gérczei <tgerczei@noreply.gitea.io>
Co-committed-by: Tamás Gérczei <tgerczei@noreply.gitea.io>
2021-03-30 19:13:23 +08:00
0bf844018c Add tea pr merge (#348)
fixes #343

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/348
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-03-18 03:56:05 +08:00
757 changed files with 31416 additions and 24900 deletions

View File

@ -1,5 +1,39 @@
# Changelog # Changelog
## [v0.8.0](https://gitea.com/gitea/tea/releases/tag/v0.8.0) - 2021-09-22
* BREAKING
* `tea notifications --all` has moved to `tea notifications --mine` (#389)
* `tea notifications` now only works with the context of a remote repo. (#389)
To run this outside of a local git dir, run either tea n `--mine` or `tea n --repo <my/repo>`
* FEATURES
* Add `tea pr merge` (#348)
* BUGFIXES
* Don't skip reading the local repo when `--repo` specifies a repo slug (#398)
* Fix adding login without token on private instances (#392)
* Correctly match login by ssh host with port (#391)
* Fix printing issue deadline (#388)
* Return useful error on wrong sshkey path (#374)
* Fix parsing of `--description` for issue/pr create (#371)
* Add missing flags (#369)
* Check negative limit command parameter (#358) (#359)
* Add missing flags to org & labels subcommands (#357)
* ENHANCEMENTS
* Don't require a body for comment PR reviews (#399)
* Accept more main branch names for login detection (#396)
* Make local repo optional for `tea pr create`(#393)
* Notifications Add State Field (#384)
* Improve error messages (#370)
* Add tab completion for fish shell (#364)
* Text editor selection: follow unix defacto standards (#356)
* MISC
* Update Dependencies (#390)
## [v0.7.1](https://gitea.com/gitea/tea/releases/tag/v0.7.1) - 2021-08-27
* BUILD
* Enable release builds for darwin/arm64 (#360)
## [v0.7.0](https://gitea.com/gitea/tea/releases/tag/v0.7.0) - 2021-03-12 ## [v0.7.0](https://gitea.com/gitea/tea/releases/tag/v0.7.0) - 2021-03-12
* BREAKING * BREAKING

25
Dockerfile Normal file
View File

@ -0,0 +1,25 @@
ARG GOVERSION="1.16.2"
FROM golang:${GOVERSION}-alpine AS buildenv
ARG CGO_ENABLED="0"
ARG GOOS="linux"
COPY . $GOPATH/src/
WORKDIR $GOPATH/src
RUN apk add --quiet --no-cache \
make \
git && \
make build
FROM scratch
ARG VERSION="0.7.0"
LABEL org.opencontainers.image.title="tea - CLI for Gitea - git with a cup of tea"
LABEL org.opencontainers.image.description="A command line tool to interact with Gitea servers"
LABEL org.opencontainers.image.version="${VERSION}"
LABEL org.opencontainers.image.authors="Tamás Gérczei <tamas@gerczei.eu>"
LABEL org.opencontainers.image.vendor="The Gitea Authors"
COPY --from=buildenv /go/src/tea /
ENV HOME="/app"
ENTRYPOINT ["/tea"]

View File

@ -38,6 +38,8 @@ else
TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
endif endif
TEA_VERSION_TAG ?= $(shell sed 's/+/_/' <<< $(TEA_VERSION))
LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)" LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)"
PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/) PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
@ -139,6 +141,10 @@ build: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES) $(EXECUTABLE): $(SOURCES)
$(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@ $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
.PHONY: build-image
build-image:
docker build --build-arg VERSION=$(TEA_VERSION) -t gitea/tea:$(TEA_VERSION_TAG) .
.PHONY: release .PHONY: release
release: release-dirs release-os release-compress release-check release: release-dirs release-os release-compress release-check

View File

@ -22,7 +22,7 @@ var CmdAutocomplete = cli.Command{
Category: catSetup, Category: catSetup,
Usage: "Install shell completion for tea", Usage: "Install shell completion for tea",
Description: "Install shell completion for tea", Description: "Install shell completion for tea",
ArgsUsage: "<shell type> (bash, zsh, powershell)", ArgsUsage: "<shell type> (bash, zsh, powershell, fish)",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "install", Name: "install",
@ -40,20 +40,26 @@ func runAutocompleteAdd(ctx *cli.Context) error {
case "zsh": case "zsh":
remoteFile = "contrib/autocomplete.zsh" remoteFile = "contrib/autocomplete.zsh"
localFile = "autocomplete.zsh" localFile = "autocomplete.zsh"
cmds = "echo 'PROG=tea _CLI_ZSH_AUTOCOMPLETE_HACK=1 source %s' >> ~/.zshrc && source ~/.zshrc" cmds = "echo 'PROG=tea _CLI_ZSH_AUTOCOMPLETE_HACK=1 source \"%s\"' >> ~/.zshrc && source ~/.zshrc"
case "bash": case "bash":
remoteFile = "contrib/autocomplete.sh" remoteFile = "contrib/autocomplete.sh"
localFile = "autocomplete.sh" localFile = "autocomplete.sh"
cmds = "echo 'PROG=tea source %s' >> ~/.bashrc && source ~/.bashrc" cmds = "echo 'PROG=tea source \"%s\"' >> ~/.bashrc && source ~/.bashrc"
case "powershell": case "powershell":
remoteFile = "contrib/autocomplete.ps1" remoteFile = "contrib/autocomplete.ps1"
localFile = "tea.ps1" localFile = "tea.ps1"
cmds = "\"& %s\" >> $profile" cmds = "\"& %s\" >> $profile"
case "fish":
// fish is different, in that urfave/cli provides a generator for the shell script needed.
// this also means that the fish completion can become out of sync with the tea binary!
// writing to this directory suffices, as fish reads files there on startup, no cmds needed.
return writeFishAutoCompleteFile(ctx)
default: default:
return fmt.Errorf("Must specify valid shell type") return fmt.Errorf("Must specify valid %s", ctx.Command.ArgsUsage)
} }
localPath, err := xdg.ConfigFile("tea/" + localFile) localPath, err := xdg.ConfigFile("tea/" + localFile)
@ -62,8 +68,7 @@ func runAutocompleteAdd(ctx *cli.Context) error {
} }
cmds = fmt.Sprintf(cmds, localPath) cmds = fmt.Sprintf(cmds, localPath)
if err = writeRemoteAutoCompleteFile(remoteFile, localPath); err != nil {
if err := saveAutoCompleteFile(remoteFile, localPath); err != nil {
return err return err
} }
@ -85,7 +90,7 @@ func runAutocompleteAdd(ctx *cli.Context) error {
return nil return nil
} }
func saveAutoCompleteFile(file, destPath string) error { func writeRemoteAutoCompleteFile(file, destPath string) error {
url := fmt.Sprintf("https://gitea.com/gitea/tea/raw/branch/master/%s", file) url := fmt.Sprintf("https://gitea.com/gitea/tea/raw/branch/master/%s", file)
fmt.Println("Fetching " + url) fmt.Println("Fetching " + url)
@ -104,3 +109,30 @@ func saveAutoCompleteFile(file, destPath string) error {
_, err = io.Copy(writer, res.Body) _, err = io.Copy(writer, res.Body)
return err return err
} }
func writeFishAutoCompleteFile(ctx *cli.Context) error {
// NOTE: to make sure this file is in sync with tea commands, we'd need to
// - check if the file exists
// - if it does, check if the tea version that wrote it is the currently running version
// - if not, rewrite the file
// on each application run
// NOTE: this generates a completion that also suggests file names, which looks kinda messy..
script, err := ctx.App.ToFishCompletion()
if err != nil {
return err
}
localPath, err := xdg.ConfigFile("fish/conf.d/tea_completion.fish")
if err != nil {
return err
}
writer, err := os.Create(localPath)
if err != nil {
return err
}
if _, err = io.WriteString(writer, script); err != nil {
return err
}
fmt.Printf("Installed tab completion to %s\n", localPath)
return nil
}

49
cmd/flags/csvflag.go Normal file
View File

@ -0,0 +1,49 @@
// 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 flags
import (
"fmt"
"strings"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CsvFlag is a wrapper around cli.StringFlag, with an added GetValues() method
// to retrieve comma separated string values as a slice.
type CsvFlag struct {
cli.StringFlag
AvailableFields []string
}
// NewCsvFlag creates a CsvFlag, while setting its usage string and default values
func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag {
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:
%s
`, usage, strings.Join(availableValues, ",")),
},
}
}
// GetValues returns the value of the flag, parsed as a commaseparated list
func (f CsvFlag) GetValues(ctx *cli.Context) ([]string, error) {
val := ctx.String(f.Name)
selection := strings.Split(val, ",")
if f.AvailableFields != nil && val != "" {
for _, field := range selection {
if !utils.Contains(f.AvailableFields, field) {
return nil, fmt.Errorf("Invalid field '%s'", field)
}
}
}
return selection, nil
}

View File

@ -11,7 +11,6 @@ import (
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils"
"github.com/araddon/dateparse" "github.com/araddon/dateparse"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -101,6 +100,27 @@ var IssuePRFlags = append([]cli.Flag{
&PaginationLimitFlag, &PaginationLimitFlag,
}, AllDefaultFlags...) }, AllDefaultFlags...)
// NotificationFlags defines flags that should be available on notifications.
var NotificationFlags = append([]cli.Flag{
NotificationStateFlag,
&cli.BoolFlag{
Name: "mine",
Aliases: []string{"m"},
Usage: "Show notifications across all your repositories instead of the current repository only",
},
&PaginationPageFlag,
&PaginationLimitFlag,
}, AllDefaultFlags...)
// NotificationStateFlag is a csv flag applied to all notification subcommands as filter
var NotificationStateFlag = NewCsvFlag(
"states",
"notification states to filter by",
[]string{"s"},
[]string{"pinned", "unread", "read"},
[]string{"unread", "pinned"},
)
// IssuePREditFlags defines flags for properties of issues and PRs // IssuePREditFlags defines flags for properties of issues and PRs
var IssuePREditFlags = append([]cli.Flag{ var IssuePREditFlags = append([]cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
@ -137,7 +157,7 @@ var IssuePREditFlags = append([]cli.Flag{
func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) { func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
opts := gitea.CreateIssueOption{ opts := gitea.CreateIssueOption{
Title: ctx.String("title"), Title: ctx.String("title"),
Body: ctx.String("body"), Body: ctx.String("description"),
Assignees: strings.Split(ctx.String("assignees"), ","), Assignees: strings.Split(ctx.String("assignees"), ","),
} }
var err error var err error
@ -178,28 +198,7 @@ func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, err
} }
// FieldsFlag generates a flag selecting printable fields. // FieldsFlag generates a flag selecting printable fields.
// To retrieve the value, use GetFields() // To retrieve the value, use f.GetValues()
func FieldsFlag(availableFields, defaultFields []string) *cli.StringFlag { func FieldsFlag(availableFields, defaultFields []string) *CsvFlag {
return &cli.StringFlag{ return NewCsvFlag("fields", "fields to print", []string{"f"}, availableFields, defaultFields)
Name: "fields",
Aliases: []string{"f"},
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
%s
`, strings.Join(availableFields, ",")),
Value: strings.Join(defaultFields, ","),
}
}
// GetFields parses the values provided in a fields flag, and
// optionally validates against valid values.
func GetFields(ctx *cli.Context, validFields []string) ([]string, error) {
selection := strings.Split(ctx.String("fields"), ",")
if validFields != nil {
for _, field := range selection {
if !utils.Contains(validFields, field) {
return nil, fmt.Errorf("Invalid field '%s'", field)
}
}
}
return selection, nil
} }

View File

@ -13,6 +13,10 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var issueFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{
"index", "title", "state", "author", "milestone", "labels",
})
// CmdIssuesList represents a sub command of issues to list issues // CmdIssuesList represents a sub command of issues to list issues
var CmdIssuesList = cli.Command{ var CmdIssuesList = cli.Command{
Name: "list", Name: "list",
@ -20,11 +24,7 @@ var CmdIssuesList = cli.Command{
Usage: "List issues of the repository", Usage: "List issues of the repository",
Description: `List issues of the repository`, Description: `List issues of the repository`,
Action: RunIssuesList, Action: RunIssuesList,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssuePRFlags...),
flags.FieldsFlag(print.IssueFields, []string{
"index", "title", "state", "author", "milestone", "labels",
}),
}, flags.IssuePRFlags...),
} }
// RunIssuesList list issues // RunIssuesList list issues
@ -52,7 +52,7 @@ func RunIssuesList(cmd *cli.Context) error {
return err return err
} }
fields, err := flags.GetFields(cmd, print.IssueFields) fields, err := issueFieldsFlag.GetValues(cmd)
if err != nil { if err != nil {
return err return err
} }

View File

@ -25,6 +25,7 @@ var CmdLabels = cli.Command{
&labels.CmdLabelUpdate, &labels.CmdLabelUpdate,
&labels.CmdLabelDelete, &labels.CmdLabelDelete,
}, },
Flags: labels.CmdLabelsList.Flags,
} }
func runLabels(ctx *cli.Context) error { func runLabels(ctx *cli.Context) error {

View File

@ -10,6 +10,7 @@ import (
"os" "os"
"strings" "strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
@ -23,7 +24,7 @@ var CmdLabelCreate = cli.Command{
Usage: "Create a label", Usage: "Create a label",
Description: `Create a label`, Description: `Create a label`,
Action: runLabelCreate, Action: runLabelCreate,
Flags: []cli.Flag{ Flags: append([]cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Usage: "label name", Usage: "label name",
@ -40,7 +41,7 @@ var CmdLabelCreate = cli.Command{
Name: "file", Name: "file",
Usage: "indicate a label file", Usage: "indicate a label file",
}, },
}, }, flags.AllDefaultFlags...),
} }
func runLabelCreate(cmd *cli.Context) error { func runLabelCreate(cmd *cli.Context) error {

View File

@ -5,6 +5,7 @@
package labels package labels
import ( import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -17,12 +18,12 @@ var CmdLabelDelete = cli.Command{
Usage: "Delete a label", Usage: "Delete a label",
Description: `Delete a label`, Description: `Delete a label`,
Action: runLabelDelete, Action: runLabelDelete,
Flags: []cli.Flag{ Flags: append([]cli.Flag{
&cli.IntFlag{ &cli.IntFlag{
Name: "id", Name: "id",
Usage: "label id", Usage: "label id",
}, },
}, }, flags.AllDefaultFlags...),
} }
func runLabelDelete(cmd *cli.Context) error { func runLabelDelete(cmd *cli.Context) error {

View File

@ -5,6 +5,7 @@
package labels package labels
import ( import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
@ -17,7 +18,7 @@ var CmdLabelUpdate = cli.Command{
Usage: "Update a label", Usage: "Update a label",
Description: `Update a label`, Description: `Update a label`,
Action: runLabelUpdate, Action: runLabelUpdate,
Flags: []cli.Flag{ Flags: append([]cli.Flag{
&cli.IntFlag{ &cli.IntFlag{
Name: "id", Name: "id",
Usage: "label id", Usage: "label id",
@ -34,7 +35,7 @@ var CmdLabelUpdate = cli.Command{
Name: "description", Name: "description",
Usage: "label description", Usage: "label description",
}, },
}, }, flags.AllDefaultFlags...),
} }
func runLabelUpdate(cmd *cli.Context) error { func runLabelUpdate(cmd *cli.Context) error {

View File

@ -16,6 +16,10 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var msIssuesFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{
"index", "kind", "title", "state", "updated", "labels",
})
// CmdMilestonesIssues represents a sub command of milestones to manage issue/pull of an milestone // CmdMilestonesIssues represents a sub command of milestones to manage issue/pull of an milestone
var CmdMilestonesIssues = cli.Command{ var CmdMilestonesIssues = cli.Command{
Name: "issues", Name: "issues",
@ -40,9 +44,7 @@ var CmdMilestonesIssues = cli.Command{
}, },
&flags.PaginationPageFlag, &flags.PaginationPageFlag,
&flags.PaginationLimitFlag, &flags.PaginationLimitFlag,
flags.FieldsFlag(print.IssueFields, []string{ msIssuesFieldsFlag,
"index", "kind", "title", "state", "updated", "labels",
}),
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
@ -110,7 +112,7 @@ func runMilestoneIssueList(cmd *cli.Context) error {
return err return err
} }
fields, err := flags.GetFields(cmd, print.IssueFields) fields, err := msIssuesFieldsFlag.GetValues(cmd)
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,11 +5,8 @@
package cmd package cmd
import ( import (
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/notifications"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -19,65 +16,14 @@ var CmdNotifications = cli.Command{
Aliases: []string{"notification", "n"}, Aliases: []string{"notification", "n"},
Category: catHelpers, Category: catHelpers,
Usage: "Show notifications", Usage: "Show notifications",
Description: "Show notifications, by default based of the current repo and unread one", Description: "Show notifications, by default based on the current repo if available",
Action: runNotifications, Action: notifications.RunNotificationsList,
Flags: append([]cli.Flag{ Subcommands: []*cli.Command{
&cli.BoolFlag{ &notifications.CmdNotificationsList,
Name: "all", &notifications.CmdNotificationsMarkRead,
Aliases: []string{"a"}, &notifications.CmdNotificationsMarkUnread,
Usage: "show all notifications of related gitea instance", &notifications.CmdNotificationsMarkPinned,
}, &notifications.CmdNotificationsUnpin,
&cli.BoolFlag{ },
Name: "read", Flags: notifications.CmdNotificationsList.Flags,
Aliases: []string{"rd"},
Usage: "show read notifications instead unread",
},
&cli.BoolFlag{
Name: "pinned",
Aliases: []string{"pd"},
Usage: "show pinned notifications instead unread",
},
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
func runNotifications(cmd *cli.Context) error {
var news []*gitea.NotificationThread
var err error
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
listOpts := ctx.GetListOptions()
if listOpts.Page == 0 {
listOpts.Page = 1
}
var status []gitea.NotifyStatus
if ctx.Bool("read") {
status = []gitea.NotifyStatus{gitea.NotifyStatusRead}
}
if ctx.Bool("pinned") {
status = append(status, gitea.NotifyStatusPinned)
}
if ctx.Bool("all") {
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
ListOptions: listOpts,
Status: status,
})
} else {
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
news, _, err = client.ListRepoNotifications(ctx.Owner, ctx.Repo, gitea.ListNotificationOptions{
ListOptions: listOpts,
Status: status,
})
}
if err != nil {
return err
}
print.NotificationsList(news, ctx.Output, ctx.Bool("all"))
return nil
} }

89
cmd/notifications/list.go Normal file
View File

@ -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 notifications
import (
"log"
"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 notifTypeFlag = flags.NewCsvFlag("types", "subject types to filter by", []string{"t"},
[]string{"issue", "pull", "repository", "commit"}, nil)
// CmdNotificationsList represents a sub command of notifications to list notifications
var CmdNotificationsList = cli.Command{
Name: "ls",
Aliases: []string{"list"},
Usage: "List notifications",
Description: `List notifications`,
Action: RunNotificationsList,
Flags: append([]cli.Flag{notifTypeFlag}, flags.NotificationFlags...),
}
// RunNotificationsList list notifications
func RunNotificationsList(ctx *cli.Context) error {
var states []gitea.NotifyStatus
statesStr, err := flags.NotificationStateFlag.GetValues(ctx)
if err != nil {
return err
}
for _, s := range statesStr {
states = append(states, gitea.NotifyStatus(s))
}
var types []gitea.NotifySubjectType
typesStr, err := notifTypeFlag.GetValues(ctx)
if err != nil {
return err
}
for _, t := range typesStr {
types = append(types, gitea.NotifySubjectType(t))
}
return listNotifications(ctx, states, types)
}
// listNotifications will get the notifications based on status and subject type
func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects []gitea.NotifySubjectType) error {
var news []*gitea.NotificationThread
var err error
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
all := ctx.Bool("mine")
// This enforces pagination (see https://github.com/go-gitea/gitea/issues/16733)
listOpts := ctx.GetListOptions()
if listOpts.Page == 0 {
listOpts.Page = 1
}
if all {
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
ListOptions: listOpts,
Status: status,
SubjectTypes: subjects,
})
} else {
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
news, _, err = client.ListRepoNotifications(ctx.Owner, ctx.Repo, gitea.ListNotificationOptions{
ListOptions: listOpts,
Status: status,
SubjectTypes: subjects,
})
}
if err != nil {
log.Fatal(err)
}
print.NotificationsList(news, ctx.Output, all)
return nil
}

View File

@ -0,0 +1,139 @@
// 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 notifications
import (
"fmt"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CmdNotificationsMarkRead represents a sub command of notifications to list read notifications
var CmdNotificationsMarkRead = cli.Command{
Name: "read",
Aliases: []string{"r"},
Usage: "Mark all filtered or a specific notification as read",
Description: "Mark all filtered or a specific notification as read",
ArgsUsage: "[all | <notification id>]",
Flags: flags.NotificationFlags,
Action: func(ctx *cli.Context) error {
cmd := context.InitCommand(ctx)
filter, err := flags.NotificationStateFlag.GetValues(ctx)
if err != nil {
return err
}
if !flags.NotificationStateFlag.IsSet() {
filter = []string{string(gitea.NotifyStatusUnread)}
}
return markNotificationAs(cmd, filter, gitea.NotifyStatusRead)
},
}
// CmdNotificationsMarkUnread will mark notifications as unread.
var CmdNotificationsMarkUnread = cli.Command{
Name: "unread",
Aliases: []string{"u"},
Usage: "Mark all filtered or a specific notification as unread",
Description: "Mark all filtered or a specific notification as unread",
ArgsUsage: "[all | <notification id>]",
Flags: flags.NotificationFlags,
Action: func(ctx *cli.Context) error {
cmd := context.InitCommand(ctx)
filter, err := flags.NotificationStateFlag.GetValues(ctx)
if err != nil {
return err
}
if !flags.NotificationStateFlag.IsSet() {
filter = []string{string(gitea.NotifyStatusRead)}
}
return markNotificationAs(cmd, filter, gitea.NotifyStatusUnread)
},
}
// CmdNotificationsMarkPinned will mark notifications as unread.
var CmdNotificationsMarkPinned = cli.Command{
Name: "pin",
Aliases: []string{"p"},
Usage: "Mark all filtered or a specific notification as pinned",
Description: "Mark all filtered or a specific notification as pinned",
ArgsUsage: "[all | <notification id>]",
Flags: flags.NotificationFlags,
Action: func(ctx *cli.Context) error {
cmd := context.InitCommand(ctx)
filter, err := flags.NotificationStateFlag.GetValues(ctx)
if err != nil {
return err
}
if !flags.NotificationStateFlag.IsSet() {
filter = []string{string(gitea.NotifyStatusUnread)}
}
return markNotificationAs(cmd, filter, gitea.NotifyStatusPinned)
},
}
// CmdNotificationsUnpin will mark pinned notifications as unread.
var CmdNotificationsUnpin = cli.Command{
Name: "unpin",
Usage: "Unpin all pinned or a specific notification",
Description: "Marks all pinned or a specific notification as read",
ArgsUsage: "[all | <notification id>]",
Flags: flags.NotificationFlags,
Action: func(ctx *cli.Context) error {
cmd := context.InitCommand(ctx)
filter := []string{string(gitea.NotifyStatusPinned)}
// NOTE: we implicitly mark it as read, to match web UI semantics. marking as unread might be more useful?
return markNotificationAs(cmd, filter, gitea.NotifyStatusRead)
},
}
func markNotificationAs(cmd *context.TeaContext, filterStates []string, targetState gitea.NotifyStatus) (err error) {
client := cmd.Login.Client()
subject := cmd.Args().First()
allRepos := cmd.Bool("mine")
states := []gitea.NotifyStatus{}
for _, s := range filterStates {
states = append(states, gitea.NotifyStatus(s))
}
switch subject {
case "", "all":
opts := gitea.MarkNotificationOptions{Status: states, ToStatus: targetState}
if allRepos {
_, err = client.ReadNotifications(opts)
} else {
cmd.Ensure(context.CtxRequirement{RemoteRepo: true})
_, err = client.ReadRepoNotifications(cmd.Owner, cmd.Repo, opts)
}
// TODO: print all affected notification subject URLs
// (not supported by API currently, https://github.com/go-gitea/gitea/issues/16797)
default:
id, err := utils.ArgToIndex(subject)
if err != nil {
return err
}
_, err = client.ReadNotification(id, targetState)
if err != nil {
return err
}
n, _, err := client.GetNotification(id)
if err != nil {
return err
}
// FIXME: this is an API URL, we want to display a web ui link..
fmt.Println(n.Subject.URL)
return nil
}
return err
}

View File

@ -25,6 +25,7 @@ var CmdOrgs = cli.Command{
&organizations.CmdOrganizationList, &organizations.CmdOrganizationList,
&organizations.CmdOrganizationDelete, &organizations.CmdOrganizationDelete,
}, },
Flags: organizations.CmdOrganizationList.Flags,
} }
func runOrganizations(ctx *cli.Context) error { func runOrganizations(ctx *cli.Context) error {

View File

@ -7,6 +7,7 @@ package organizations
import ( import (
"fmt" "fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -19,6 +20,10 @@ var CmdOrganizationDelete = cli.Command{
Description: "Delete users organizations", Description: "Delete users organizations",
ArgsUsage: "<organization name>", ArgsUsage: "<organization name>",
Action: RunOrganizationDelete, Action: RunOrganizationDelete,
Flags: []cli.Flag{
&flags.LoginFlag,
&flags.RemoteFlag,
},
} }
// RunOrganizationDelete delete user organization // RunOrganizationDelete delete user organization

View File

@ -43,6 +43,7 @@ var CmdPulls = cli.Command{
&pulls.CmdPullsReview, &pulls.CmdPullsReview,
&pulls.CmdPullsApprove, &pulls.CmdPullsApprove,
&pulls.CmdPullsReject, &pulls.CmdPullsReject,
&pulls.CmdPullsMerge,
}, },
} }

View File

@ -35,11 +35,10 @@ var CmdPullsCreate = cli.Command{
func runPullsCreate(cmd *cli.Context) error { func runPullsCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{LocalRepo: true})
// no args -> interactive mode // no args -> interactive mode
if ctx.NumFlags() == 0 { if ctx.NumFlags() == 0 {
return interact.CreatePull(ctx.Login, ctx.Owner, ctx.Repo) return interact.CreatePull(ctx)
} }
// else use args to create PR // else use args to create PR
@ -49,9 +48,7 @@ func runPullsCreate(cmd *cli.Context) error {
} }
return task.CreatePull( return task.CreatePull(
ctx.Login, ctx,
ctx.Owner,
ctx.Repo,
ctx.String("base"), ctx.String("base"),
ctx.String("head"), ctx.String("head"),
opts, opts,

70
cmd/pulls/merge.go Normal file
View File

@ -0,0 +1,70 @@
// 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 pulls
import (
"fmt"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CmdPullsMerge merges a PR
var CmdPullsMerge = cli.Command{
Name: "merge",
Aliases: []string{"m"},
Usage: "Merge a pull request",
Description: "Merge a pull request",
ArgsUsage: "<pull index>",
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "style",
Aliases: []string{"s"},
Usage: "Kind of merge to perform: merge, rebase, squash, rebase-merge",
Value: "merge",
},
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
Usage: "Merge commit title",
},
&cli.StringFlag{
Name: "message",
Aliases: []string{"m"},
Usage: "Merge commit message",
},
}, flags.AllDefaultFlags...),
Action: func(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() != 1 {
return fmt.Errorf("Must specify a PR index")
}
idx, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
success, _, err := ctx.Login.Client().MergePullRequest(ctx.Owner, ctx.Repo, idx, gitea.MergePullRequestOption{
Style: gitea.MergeStyle(ctx.String("style")),
Title: ctx.String("title"),
Message: ctx.String("message"),
})
if err != nil {
return err
}
if !success {
return fmt.Errorf("Failed to merge PR. Is it still open?")
}
return nil
},
}

View File

@ -13,6 +13,10 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var repoFieldsFlag = flags.FieldsFlag(print.RepoFields, []string{
"owner", "name", "type", "ssh",
})
// CmdReposListFlags contains all flags needed for repo listing // CmdReposListFlags contains all flags needed for repo listing
var CmdReposListFlags = append([]cli.Flag{ var CmdReposListFlags = append([]cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
@ -27,9 +31,7 @@ var CmdReposListFlags = append([]cli.Flag{
Required: false, Required: false,
Usage: "List your starred repos instead", Usage: "List your starred repos instead",
}, },
flags.FieldsFlag(print.RepoFields, []string{ repoFieldsFlag,
"owner", "name", "type", "ssh",
}),
&typeFilterFlag, &typeFilterFlag,
&flags.PaginationPageFlag, &flags.PaginationPageFlag,
&flags.PaginationLimitFlag, &flags.PaginationLimitFlag,
@ -82,7 +84,7 @@ func RunReposList(cmd *cli.Context) error {
reposFiltered = filterReposByType(rps, typeFilter) reposFiltered = filterReposByType(rps, typeFilter)
} }
fields, err := flags.GetFields(cmd, print.RepoFields) fields, err := repoFieldsFlag.GetValues(cmd)
if err != nil { if err != nil {
return err return err
} }

View File

@ -50,9 +50,7 @@ var CmdReposSearch = cli.Command{
Required: false, Required: false,
Usage: "Filter archived repos (true|false)", Usage: "Filter archived repos (true|false)",
}, },
flags.FieldsFlag(print.RepoFields, []string{ repoFieldsFlag,
"owner", "name", "type", "ssh",
}),
&flags.PaginationPageFlag, &flags.PaginationPageFlag,
&flags.PaginationLimitFlag, &flags.PaginationLimitFlag,
}, flags.LoginOutputFlags...), }, flags.LoginOutputFlags...),
@ -125,7 +123,7 @@ func runReposSearch(cmd *cli.Context) error {
return err return err
} }
fields, err := flags.GetFields(cmd, nil) fields, err := repoFieldsFlag.GetValues(cmd)
if err != nil { if err != nil {
return err return err
} }

View File

@ -19,6 +19,17 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
// NOTE: not using NewCsvFlag, as we don't want an alias & default value.
var timeFieldsFlag = &flags.CsvFlag{
AvailableFields: print.TrackedTimeFields,
StringFlag: cli.StringFlag{
Name: "fields",
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
%s
`, strings.Join(print.TrackedTimeFields, ",")),
},
}
// CmdTrackedTimesList represents a sub command of times to list them // CmdTrackedTimesList represents a sub command of times to list them
var CmdTrackedTimesList = cli.Command{ var CmdTrackedTimesList = cli.Command{
Name: "list", Name: "list",
@ -53,12 +64,7 @@ Depending on your permissions on the repository, only your own tracked times mig
Aliases: []string{"m"}, Aliases: []string{"m"},
Usage: "Show all times tracked by you across all repositories (overrides command arguments)", Usage: "Show all times tracked by you across all repositories (overrides command arguments)",
}, },
&cli.StringFlag{ timeFieldsFlag,
Name: "fields",
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
%s
`, strings.Join(print.TrackedTimeFields, ",")),
},
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
@ -116,7 +122,7 @@ func RunTimesList(cmd *cli.Context) error {
} }
if ctx.IsSet("fields") { if ctx.IsSet("fields") {
if fields, err = flags.GetFields(cmd, print.TrackedTimeFields); err != nil { if fields, err = timeFieldsFlag.GetValues(cmd); err != nil {
return err return err
} }
} }

46
go.mod
View File

@ -4,35 +4,37 @@ go 1.13
require ( require (
code.gitea.io/gitea-vet v0.2.1 code.gitea.io/gitea-vet v0.2.1
code.gitea.io/sdk/gitea v0.13.1-0.20210304201955-ff82113459b5 code.gitea.io/sdk/gitea v0.15.0
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b
github.com/AlecAivazis/survey/v2 v2.2.8 github.com/AlecAivazis/survey/v2 v2.3.1
github.com/Microsoft/go-winio v0.4.16 // indirect github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c // indirect
github.com/adrg/xdg v0.3.1 github.com/adrg/xdg v0.3.3
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e github.com/alecthomas/chroma v0.9.2 // indirect
github.com/charmbracelet/glamour v0.2.0 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/charmbracelet/glamour v0.3.0
github.com/go-git/go-git/v5 v5.2.0 github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/imdario/mergo v0.3.11 // indirect github.com/go-git/go-git/v5 v5.4.2
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/hashicorp/go-version v1.3.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/kevinburke/ssh_config v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/muesli/termenv v0.7.4 github.com/microcosm-cc/bluemonday v1.0.15 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.9.0
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/rivo/uniseg v0.2.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/urfave/cli/v2 v2.3.0 github.com/urfave/cli/v2 v2.3.0
github.com/xanzy/ssh-agent v0.3.0 // indirect github.com/xanzy/ssh-agent v0.3.1 // indirect
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 github.com/yuin/goldmark v1.4.0 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3 // indirect golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
golang.org/x/text v0.3.5 // indirect golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/tools v0.1.0 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.5 // indirect
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )
replace github.com/charmbracelet/glamour => github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2 replace github.com/charmbracelet/glamour => github.com/noerw/glamour v0.3.0-patch

176
go.sum
View File

@ -1,25 +1,30 @@
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s= code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
code.gitea.io/sdk/gitea v0.13.1-0.20210304201955-ff82113459b5 h1:va0KddYHN8bH6MCUaWf5e4p+il55blUw5J0ha5vTMaQ= code.gitea.io/sdk/gitea v0.15.0 h1:tsNhxDM/2N1Ohv1Xq5UWrht/esg0WmtRj4wsHVHriTg=
code.gitea.io/sdk/gitea v0.13.1-0.20210304201955-ff82113459b5/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs= code.gitea.io/sdk/gitea v0.15.0/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b h1:CLYsMGcGLohESQDMth+RgJ4cB3CCHToxnj0zBbvB3sE= gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b h1:CLYsMGcGLohESQDMth+RgJ4cB3CCHToxnj0zBbvB3sE=
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b/go.mod h1:Fc8iyPm4NINRWujeIk2bTfcbGc4ZYY29/oMAAGcr4qI= gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b/go.mod h1:Fc8iyPm4NINRWujeIk2bTfcbGc4ZYY29/oMAAGcr4qI=
github.com/AlecAivazis/survey/v2 v2.2.8 h1:TgxCwybKdBckmC+/P9/5h49rw/nAHe/itZL0dgHs+Q0= github.com/AlecAivazis/survey/v2 v2.3.1 h1:lzkuHA60pER7L4eYL8qQJor4bUWlJe4V0gqAT19tdOA=
github.com/AlecAivazis/survey/v2 v2.2.8/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk= github.com/AlecAivazis/survey/v2 v2.3.1/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/adrg/xdg v0.3.1 h1:uIyL9BYfXaFgDyVRKE8wjtm6ETQULweQqTofphEFJYY= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/adrg/xdg v0.3.1/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ= github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c h1:FP7mMdsXy0ybzar1sJeIcZtaJka0U/ZmLTW4wRpolYk=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/adrg/xdg v0.3.3 h1:s/tV7MdqQnzB1nKY8aqHvAMD+uCiuEDzVB5HLRY849U=
github.com/adrg/xdg v0.3.3/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE= github.com/alecthomas/chroma v0.8.2/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
github.com/alecthomas/chroma v0.8.1/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= github.com/alecthomas/chroma v0.9.2 h1:yU1sE2+TZbLIQPMk30SolL2Hn53SR/Pv750f7qZ/XMs=
github.com/alecthomas/chroma v0.9.2/go.mod h1:eMuEnpA18XbG/WhOWtCzJHS7WqEtDAI+HxdwoW0nVSk=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
@ -27,25 +32,25 @@ github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkx
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e h1:OjdSMCht0ZVX7IH0nTdf00xEustvbtUGRgMh3gbdmOg= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
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 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
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.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
@ -54,69 +59,76 @@ github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI= github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs= github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg= github.com/microcosm-cc/bluemonday v1.0.6/go.mod h1:HOT/6NaBlR0f9XlxD3zolN6Z3N8Lp4pvhp+jLS5ihnI=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w= github.com/microcosm-cc/bluemonday v1.0.15 h1:J4uN+qPng9rvkBZBoBb8YGR+ijuklIMpSOZZLjYpbeY=
github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3ZpqOF2+sdFr0Om+ce30=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/muesli/reflow v0.2.0 h1:2o0UBJPHHH4fa2GCXU4Rg4DwOtWPMekCeyc5EWbAQp0=
github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8= github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8=
github.com/muesli/termenv v0.7.4 h1:/pBqvU5CpkY53tU0vVn+xgs2ZTX63aH5nY+SSps5Xa8= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0=
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2 h1:ACjOTGUGi7rt3JQU9GIFFs8sueFGShy6GcGjQhMmKjs= github.com/noerw/glamour v0.3.0-patch h1:yc3wdbUIySok6KYeX5BtWnlj+PvP1uYeCeTSwq2rtSw=
github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2/go.mod h1:WIVFX8Y2VIK1Y/1qtXYL/Vvzqlcbo3VgVop9i2piPkE= github.com/noerw/glamour v0.3.0-patch/go.mod h1:TzF0koPZhqq0YVBNL100cPHznAAjVj7fksX2RInwjGw=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 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/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -134,10 +146,12 @@ github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA= github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA=
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw= github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -146,74 +160,83 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.1 h1:eVwehsLsZlCJCwXyGLgg+Q4iFWE/eTIMG0e8waCmm/I= github.com/yuin/goldmark v1.3.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0 h1:OtISOGfH6sOWa1/qXqqAiOIAO6Z5J3AEAE18WAq6BiQ=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3 h1:RdE7htvBru4I4VZQofQjCZk5W9+aLNlSF5n0zgVwm8s= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -221,8 +244,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -142,8 +142,9 @@ func AddLogin(login *Login) error {
return saveConfig() return saveConfig()
} }
// Client returns a client to operate Gitea API // Client returns a client to operate Gitea API. You may provide additional modifiers
func (l *Login) Client() *gitea.Client { // for the client like gitea.SetBasicAuth() for customization
func (l *Login) Client(options ...func(*gitea.Client)) *gitea.Client {
httpClient := &http.Client{} httpClient := &http.Client{}
if l.Insecure { if l.Insecure {
cookieJar, _ := cookiejar.New(nil) cookieJar, _ := cookiejar.New(nil)
@ -155,10 +156,9 @@ func (l *Login) Client() *gitea.Client {
}} }}
} }
client, err := gitea.NewClient(l.URL, options = append(options, gitea.SetToken(l.Token), gitea.SetHTTPClient(httpClient))
gitea.SetToken(l.Token),
gitea.SetHTTPClient(httpClient), client, err := gitea.NewClient(l.URL, options...)
)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -39,6 +39,9 @@ type TeaContext struct {
func (ctx *TeaContext) GetListOptions() gitea.ListOptions { func (ctx *TeaContext) GetListOptions() gitea.ListOptions {
page := ctx.Int("page") page := ctx.Int("page")
limit := ctx.Int("limit") limit := ctx.Int("limit")
if limit < 0 {
limit = 0
}
if limit != 0 && page == 0 { if limit != 0 && page == 0 {
page = 1 page = 1
} }
@ -96,14 +99,13 @@ func InitCommand(ctx *cli.Context) *TeaContext {
} }
} }
if len(repoFlag) == 0 || repoFlagPathExists { // try to read local git repo & extract context: if repoFlag specifies a valid path, read repo in that dir,
// try to read git repo & extract context, ignoring if PWD is not a repo // otherwise attempt PWD. if no repo is found, continue with default login
if c.LocalRepo, c.Login, c.RepoSlug, err = contextFromLocalRepo(repoPath, remoteFlag); err != nil { if c.LocalRepo, c.Login, c.RepoSlug, err = contextFromLocalRepo(repoPath, remoteFlag); err != nil {
if err == errNotAGiteaRepo || err == gogit.ErrRepositoryNotExists { if err == errNotAGiteaRepo || err == gogit.ErrRepositoryNotExists {
// we can deal with that, commands needing the optional values use ctx.Ensure() // we can deal with that, commands needing the optional values use ctx.Ensure()
} else { } else {
log.Fatal(err.Error()) log.Fatal(err.Error())
}
} }
} }
@ -120,8 +122,16 @@ func InitCommand(ctx *cli.Context) *TeaContext {
} }
} else if c.Login == nil { } else if c.Login == nil {
if c.Login, err = config.GetDefaultLogin(); err != nil { if c.Login, err = config.GetDefaultLogin(); err != nil {
log.Fatal(err.Error()) if err.Error() == "No available login" {
// TODO: maybe we can directly start interact.CreateLogin() (only if
// we're sure we can interactively!), as gh cli does.
fmt.Println(`No gitea login configured. To start using tea, first run
tea login add
and then run your command again.`)
}
os.Exit(1)
} }
fmt.Printf("NOTE: no gitea login detected, falling back to login '%s'\n", c.Login.Name)
} }
// parse reposlug (owner falling back to login owner if reposlug contains only repo name) // parse reposlug (owner falling back to login owner if reposlug contains only repo name)
@ -155,10 +165,14 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
} }
if len(gitConfig.Remotes) > 1 { if len(gitConfig.Remotes) > 1 {
// if master branch is present, use it as the default remote // if master branch is present, use it as the default remote
masterBranch, ok := gitConfig.Branches["master"] mainBranches := []string{"main", "master", "trunk"}
if ok { for _, b := range mainBranches {
if len(masterBranch.Remote) > 0 { masterBranch, ok := gitConfig.Branches[b]
remoteValue = masterBranch.Remote if ok {
if len(masterBranch.Remote) > 0 {
remoteValue = masterBranch.Remote
}
break
} }
} }
} }
@ -174,8 +188,9 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
return repo, nil, "", err return repo, nil, "", err
} }
for _, l := range logins { for _, l := range logins {
sshHost := l.GetSSHHost()
for _, u := range remoteConfig.URLs { for _, u := range remoteConfig.URLs {
p, err := git.ParseURL(strings.TrimSpace(u)) p, err := git.ParseURL(u)
if err != nil { if err != nil {
return repo, nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error()) return repo, nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error())
} }
@ -186,8 +201,8 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
return repo, &l, strings.TrimSuffix(path, ".git"), nil return repo, &l, strings.TrimSuffix(path, ".git"), nil
} }
} else if strings.EqualFold(p.Scheme, "ssh") { } else if strings.EqualFold(p.Scheme, "ssh") {
if l.GetSSHHost() == strings.Split(p.Host, ":")[0] { if sshHost == p.Host {
return repo, &l, strings.TrimLeft(strings.TrimSuffix(p.Path, ".git"), "/"), nil return repo, &l, strings.TrimLeft(p.Path, "/"), nil
} }
} }
} }

View File

@ -55,7 +55,7 @@ func readSSHPrivKey(keyFile string, passwordCallback pwCallback) (sig ssh.Signer
} }
sshKey, err := ioutil.ReadFile(keyFile) sshKey, err := ioutil.ReadFile(keyFile)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("can not read ssh key '%s'", keyFile)
} }
sig, err = ssh.ParsePrivateKey(sshKey) sig, err = ssh.ParsePrivateKey(sshKey)
if _, ok := err.(*ssh.PassphraseMissingError); ok && passwordCallback != nil { if _, ok := err.(*ssh.PassphraseMissingError); ok && passwordCallback != nil {

View File

@ -20,6 +20,10 @@ type URLParser struct {
// Parse parses the git URL // Parse parses the git URL
func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) { 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) && if !protocolRe.MatchString(rawURL) &&
strings.Contains(rawURL, ":") && strings.Contains(rawURL, ":") &&
// not a Windows path // not a Windows path

View File

@ -6,26 +6,23 @@ package interact
import ( import (
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/git"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
) )
// CreatePull interactively creates a PR // CreatePull interactively creates a PR
func CreatePull(login *config.Login, owner, repo string) error { func CreatePull(ctx *context.TeaContext) (err error) {
var base, head string var base, head string
// owner, repo // owner, repo
owner, repo, err := promptRepoSlug(owner, repo) if ctx.Owner, ctx.Repo, err = promptRepoSlug(ctx.Owner, ctx.Repo); err != nil {
if err != nil {
return err return err
} }
// base // base
base, err = task.GetDefaultPRBase(login, owner, repo) if base, err = task.GetDefaultPRBase(ctx.Login, ctx.Owner, ctx.Repo); err != nil {
if err != nil {
return err return err
} }
promptI := &survey.Input{Message: "Target branch:", Default: base} promptI := &survey.Input{Message: "Target branch:", Default: base}
@ -34,14 +31,14 @@ func CreatePull(login *config.Login, owner, repo string) error {
} }
// head // head
localRepo, err := git.RepoForWorkdir() var headOwner, headBranch string
if err != nil {
return err
}
promptOpts := survey.WithValidator(survey.Required) promptOpts := survey.WithValidator(survey.Required)
headOwner, headBranch, err := task.GetDefaultPRHead(localRepo)
if err == nil { if ctx.LocalRepo != nil {
promptOpts = nil headOwner, headBranch, err = task.GetDefaultPRHead(ctx.LocalRepo)
if err == nil {
promptOpts = nil
}
} }
promptI = &survey.Input{Message: "Source repo owner:", Default: headOwner} promptI = &survey.Input{Message: "Source repo owner:", Default: headOwner}
if err := survey.AskOne(promptI, &headOwner); err != nil { if err := survey.AskOne(promptI, &headOwner); err != nil {
@ -52,17 +49,15 @@ func CreatePull(login *config.Login, owner, repo string) error {
return err return err
} }
head = task.GetHeadSpec(headOwner, headBranch, owner) head = task.GetHeadSpec(headOwner, headBranch, ctx.Owner)
opts := gitea.CreateIssueOption{Title: task.GetDefaultPRTitle(head)} opts := gitea.CreateIssueOption{Title: task.GetDefaultPRTitle(head)}
if err = promptIssueProperties(login, owner, repo, &opts); err != nil { if err = promptIssueProperties(ctx.Login, ctx.Owner, ctx.Repo, &opts); err != nil {
return err return err
} }
return task.CreatePull( return task.CreatePull(
login, ctx,
owner,
repo,
base, base,
head, head,
&opts) &opts)

View File

@ -52,7 +52,7 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error {
// comment // comment
var promptOpts survey.AskOpt var promptOpts survey.AskOpt
if state == gitea.ReviewStateComment || state == gitea.ReviewStateRequestChanges { if (state == gitea.ReviewStateComment && len(codeComments) == 0) || state == gitea.ReviewStateRequestChanges {
promptOpts = survey.WithValidator(survey.Required) promptOpts = survey.WithValidator(survey.Required)
} }
err = survey.AskOne(&survey.Multiline{Message: "Concluding comment:"}, &comment, promptOpts) err = survey.AskOne(&survey.Multiline{Message: "Concluding comment:"}, &comment, promptOpts)
@ -63,7 +63,7 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error {
return task.CreatePullReview(ctx, idx, state, comment, codeComments) return task.CreatePullReview(ctx, idx, state, comment, codeComments)
} }
// DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $EDITOR to comment on diff, // DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $VISUAL or $EDITOR to comment on diff,
// (3) parses resulting file into code comments. // (3) parses resulting file into code comments.
func DoDiffReview(ctx *context.TeaContext, idx int64) ([]gitea.CreatePullReviewComment, error) { func DoDiffReview(ctx *context.TeaContext, idx int64) ([]gitea.CreatePullReviewComment, error) {
tmpFile, err := task.SavePullDiff(ctx, idx) tmpFile, err := task.SavePullDiff(ctx, idx)

View File

@ -101,6 +101,9 @@ func (x printableIssue) FormatField(field string) string {
case "updated": case "updated":
return FormatTime(x.Updated) return FormatTime(x.Updated)
case "deadline": case "deadline":
if x.Deadline == nil {
return ""
}
return FormatTime(*x.Deadline) return FormatTime(*x.Deadline)
case "milestone": case "milestone":
if x.Milestone != nil { if x.Milestone != nil {

View File

@ -5,6 +5,7 @@
package print package print
import ( import (
"fmt"
"strings" "strings"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
@ -13,7 +14,10 @@ import (
// NotificationsList prints a listing of notification threads // NotificationsList prints a listing of notification threads
func NotificationsList(news []*gitea.NotificationThread, output string, showRepository bool) { func NotificationsList(news []*gitea.NotificationThread, output string, showRepository bool) {
headers := []string{ headers := []string{
"ID",
"Status",
"Type", "Type",
"State",
"Index", "Index",
"Title", "Title",
} }
@ -38,7 +42,21 @@ func NotificationsList(news []*gitea.NotificationThread, output string, showRepo
index = "#" + index index = "#" + index
} }
item := []string{n.Subject.Type, index, n.Subject.Title} status := "read"
if n.Pinned {
status = "pinned"
} else if n.Unread {
status = "unread"
}
item := []string{
fmt.Sprint(n.ID),
status,
string(n.Subject.Type),
string(n.Subject.State),
index,
n.Subject.Title,
}
if showRepository { if showRepository {
item = append(item, n.Repository.FullName) item = append(item, n.Repository.FullName)
} }

View File

@ -56,14 +56,14 @@ func CreateLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bo
Created: time.Now().Unix(), Created: time.Now().Unix(),
} }
client := login.Client()
if len(token) == 0 { if len(token) == 0 {
if login.Token, err = generateToken(client, user, passwd); err != nil { if login.Token, err = generateToken(login, user, passwd); err != nil {
return err return err
} }
} }
client := login.Client()
// Verify if authentication works and get user info // Verify if authentication works and get user info
u, _, err := client.GetMyUserInfo() u, _, err := client.GetMyUserInfo()
if err != nil { if err != nil {
@ -98,16 +98,17 @@ func CreateLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bo
} }
// generateToken creates a new token when given BasicAuth credentials // generateToken creates a new token when given BasicAuth credentials
func generateToken(client *gitea.Client, user, pass string) (string, error) { func generateToken(login config.Login, user, pass string) (string, error) {
gitea.SetBasicAuth(user, pass)(client) client := login.Client(gitea.SetBasicAuth(user, pass))
host, _ := os.Hostname()
tl, _, err := client.ListAccessTokens(gitea.ListAccessTokensOptions{}) tl, _, err := client.ListAccessTokens(gitea.ListAccessTokensOptions{})
if err != nil { if err != nil {
return "", err return "", err
} }
host, _ := os.Hostname()
tokenName := host + "-tea" tokenName := host + "-tea"
// append timestamp, if a token with this hostname already exists
for i := range tl { for i := range tl {
if tl[i].Name == tokenName { if tl[i].Name == tokenName {
tokenName += time.Now().Format("2006-01-02_15-04-05") tokenName += time.Now().Format("2006-01-02_15-04-05")

View File

@ -28,7 +28,7 @@ func PullCheckout(
client := login.Client() client := login.Client()
pr, _, err := client.GetPullRequest(repoOwner, repoName, index) pr, _, err := client.GetPullRequest(repoOwner, repoName, index)
if err != nil { if err != nil {
return err return fmt.Errorf("couldn't fetch PR: %s", err)
} }
if err := workaround.FixPullHeadSha(client, pr); err != nil { if err := workaround.FixPullHeadSha(client, pr); err != nil {
return err return err

View File

@ -10,22 +10,17 @@ import (
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/context"
local_git "code.gitea.io/tea/modules/git" local_git "code.gitea.io/tea/modules/git"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
) )
// CreatePull creates a PR in the given repo and prints the result // CreatePull creates a PR in the given repo and prints the result
func CreatePull(login *config.Login, repoOwner, repoName, base, head string, opts *gitea.CreateIssueOption) error { func CreatePull(ctx *context.TeaContext, base, head string, opts *gitea.CreateIssueOption) (err error) {
// open local git repo
localRepo, err := local_git.RepoForWorkdir()
if err != nil {
return fmt.Errorf("Could not open local repo: %s", err)
}
// default is default branch // default is default branch
if len(base) == 0 { if len(base) == 0 {
base, err = GetDefaultPRBase(login, repoOwner, repoName) base, err = GetDefaultPRBase(ctx.Login, ctx.Owner, ctx.Repo)
if err != nil { if err != nil {
return err return err
} }
@ -33,12 +28,15 @@ func CreatePull(login *config.Login, repoOwner, repoName, base, head string, opt
// default is current one // default is current one
if len(head) == 0 { if len(head) == 0 {
headOwner, headBranch, err := GetDefaultPRHead(localRepo) if ctx.LocalRepo == nil {
return fmt.Errorf("no local git repo detected, please specify head branch")
}
headOwner, headBranch, err := GetDefaultPRHead(ctx.LocalRepo)
if err != nil { if err != nil {
return err return err
} }
head = GetHeadSpec(headOwner, headBranch, repoOwner) head = GetHeadSpec(headOwner, headBranch, ctx.Owner)
} }
// head & base may not be the same // head & base may not be the same
@ -52,10 +50,10 @@ func CreatePull(login *config.Login, repoOwner, repoName, base, head string, opt
} }
// title is required // title is required
if len(opts.Title) == 0 { if len(opts.Title) == 0 {
return fmt.Errorf("Title is required") return fmt.Errorf("title is required")
} }
pr, _, err := login.Client().CreatePullRequest(repoOwner, repoName, gitea.CreatePullRequestOption{ pr, _, err := ctx.Login.Client().CreatePullRequest(ctx.Owner, ctx.Repo, gitea.CreatePullRequestOption{
Head: head, Head: head,
Base: base, Base: base,
Title: opts.Title, Title: opts.Title,
@ -67,7 +65,7 @@ func CreatePull(login *config.Login, repoOwner, repoName, base, head string, opt
}) })
if err != nil { if err != nil {
return fmt.Errorf("Could not create PR from %s to %s:%s: %s", head, repoOwner, base, err) return fmt.Errorf("could not create PR from %s to %s:%s: %s", head, ctx.Owner, base, err)
} }
print.PullDetails(pr, nil, nil) print.PullDetails(pr, nil, nil)

View File

@ -107,10 +107,13 @@ func ParseDiffComments(diffFile string) ([]gitea.CreatePullReviewComment, error)
// OpenFileInEditor opens filename in a text editor, and blocks until the editor terminates. // OpenFileInEditor opens filename in a text editor, and blocks until the editor terminates.
func OpenFileInEditor(filename string) error { func OpenFileInEditor(filename string) error {
editor := os.Getenv("EDITOR") editor := os.Getenv("VISUAL")
if editor == "" { if editor == "" {
fmt.Println("No $EDITOR env is set, defaulting to vim") editor = os.Getenv("EDITOR")
editor = "vim" if editor == "" {
fmt.Println("No $VISUAL or $EDITOR env is set, defaulting to vim")
editor = "vi"
}
} }
// Get the full executable path for the editor. // Get the full executable path for the editor.

View File

@ -39,6 +39,9 @@ func (c *Client) RunCronTasks(task string) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
return nil, err return nil, err
} }
if err := escapeValidatePathSegments(&task); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST", fmt.Sprintf("/admin/cron/%s", task), jsonHeader, nil) _, resp, err := c.getResponse("POST", fmt.Sprintf("/admin/cron/%s", task), jsonHeader, nil)
return resp, err return resp, err
} }

View File

@ -26,6 +26,9 @@ func (c *Client) AdminListOrgs(opt AdminListOrgsOptions) ([]*Organization, *Resp
// AdminCreateOrg create an organization // AdminCreateOrg create an organization
func (c *Client) AdminCreateOrg(user string, opt CreateOrgOption) (*Organization, *Response, error) { func (c *Client) AdminCreateOrg(user string, opt CreateOrgOption) (*Organization, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -12,6 +12,9 @@ import (
// AdminCreateRepo create a repo // AdminCreateRepo create a repo
func (c *Client) AdminCreateRepo(user string, opt CreateRepoOption) (*Repository, *Response, error) { func (c *Client) AdminCreateRepo(user string, opt CreateRepoOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -26,14 +26,15 @@ func (c *Client) AdminListUsers(opt AdminListUsersOptions) ([]*User, *Response,
// CreateUserOption create user options // CreateUserOption create user options
type CreateUserOption struct { type CreateUserOption struct {
SourceID int64 `json:"source_id"` SourceID int64 `json:"source_id"`
LoginName string `json:"login_name"` LoginName string `json:"login_name"`
Username string `json:"username"` Username string `json:"username"`
FullName string `json:"full_name"` FullName string `json:"full_name"`
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"` Password string `json:"password"`
MustChangePassword *bool `json:"must_change_password"` MustChangePassword *bool `json:"must_change_password"`
SendNotify bool `json:"send_notify"` SendNotify bool `json:"send_notify"`
Visibility *VisibleType `json:"visibility"`
} }
// Validate the CreateUserOption struct // Validate the CreateUserOption struct
@ -63,25 +64,31 @@ func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, *Response, error)
// EditUserOption edit user options // EditUserOption edit user options
type EditUserOption struct { type EditUserOption struct {
SourceID int64 `json:"source_id"` SourceID int64 `json:"source_id"`
LoginName string `json:"login_name"` LoginName string `json:"login_name"`
Email *string `json:"email"` Email *string `json:"email"`
FullName *string `json:"full_name"` FullName *string `json:"full_name"`
Password string `json:"password"` Password string `json:"password"`
MustChangePassword *bool `json:"must_change_password"` Description *string `json:"description"`
Website *string `json:"website"` MustChangePassword *bool `json:"must_change_password"`
Location *string `json:"location"` Website *string `json:"website"`
Active *bool `json:"active"` Location *string `json:"location"`
Admin *bool `json:"admin"` Active *bool `json:"active"`
AllowGitHook *bool `json:"allow_git_hook"` Admin *bool `json:"admin"`
AllowImportLocal *bool `json:"allow_import_local"` AllowGitHook *bool `json:"allow_git_hook"`
MaxRepoCreation *int `json:"max_repo_creation"` AllowImportLocal *bool `json:"allow_import_local"`
ProhibitLogin *bool `json:"prohibit_login"` MaxRepoCreation *int `json:"max_repo_creation"`
AllowCreateOrganization *bool `json:"allow_create_organization"` ProhibitLogin *bool `json:"prohibit_login"`
AllowCreateOrganization *bool `json:"allow_create_organization"`
Restricted *bool `json:"restricted"`
Visibility *VisibleType `json:"visibility"`
} }
// AdminEditUser modify user informations // AdminEditUser modify user informations
func (c *Client) AdminEditUser(user string, opt EditUserOption) (*Response, error) { func (c *Client) AdminEditUser(user string, opt EditUserOption) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@ -92,12 +99,18 @@ func (c *Client) AdminEditUser(user string, opt EditUserOption) (*Response, erro
// AdminDeleteUser delete one user according name // AdminDeleteUser delete one user according name
func (c *Client) AdminDeleteUser(user string) (*Response, error) { func (c *Client) AdminDeleteUser(user string) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil)
return resp, err return resp, err
} }
// AdminCreateUserPublicKey adds a public key for the user // AdminCreateUserPublicKey adds a public key for the user
func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, *Response, error) { func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -109,6 +122,9 @@ func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*Pu
// AdminDeleteUserPublicKey deletes a user's public key // AdminDeleteUserPublicKey deletes a user's public key
func (c *Client) AdminDeleteUserPublicKey(user string, keyID int) (*Response, error) { func (c *Client) AdminDeleteUserPublicKey(user string, keyID int) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s/keys/%d", user, keyID), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s/keys/%d", user, keyID), nil, nil)
return resp, err return resp, err
} }

View File

@ -31,6 +31,9 @@ type ListReleaseAttachmentsOptions struct {
// ListReleaseAttachments list release's attachments // ListReleaseAttachments list release's attachments
func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt ListReleaseAttachmentsOptions) ([]*Attachment, *Response, error) { func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt ListReleaseAttachmentsOptions) ([]*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
attachments := make([]*Attachment, 0, opt.PageSize) attachments := make([]*Attachment, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
@ -41,6 +44,9 @@ func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt Li
// GetReleaseAttachment returns the requested attachment // GetReleaseAttachment returns the requested attachment
func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, *Response, error) { func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
a := new(Attachment) a := new(Attachment)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id),
@ -50,6 +56,9 @@ func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64
// CreateReleaseAttachment creates an attachment for the given release // CreateReleaseAttachment creates an attachment for the given release
func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, *Response, error) { func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
// Write file to body // Write file to body
body := new(bytes.Buffer) body := new(bytes.Buffer)
writer := multipart.NewWriter(body) writer := multipart.NewWriter(body)
@ -80,6 +89,9 @@ type EditAttachmentOptions struct {
// EditReleaseAttachment updates the given attachment with the given options // EditReleaseAttachment updates the given attachment with the given options
func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, *Response, error) { func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&form) body, err := json.Marshal(&form)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -91,6 +103,9 @@ func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachm
// DeleteReleaseAttachment deletes the given attachment including the uploaded file // DeleteReleaseAttachment deletes the given attachment including the uploaded file
func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) (*Response, error) { func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil)
return resp, err return resp, err
} }

View File

@ -13,6 +13,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"strings" "strings"
"sync" "sync"
@ -309,3 +310,24 @@ func (c *Client) getStatusCode(method, path string, header http.Header, body io.
return resp.StatusCode, resp, nil return resp.StatusCode, resp, nil
} }
// pathEscapeSegments escapes segments of a path while not escaping forward slash
func pathEscapeSegments(path string) string {
slice := strings.Split(path, "/")
for index := range slice {
slice[index] = url.PathEscape(slice[index])
}
escapedPath := strings.Join(slice, "/")
return escapedPath
}
// escapeValidatePathSegments is a help function to validate and encode url path segments
func escapeValidatePathSegments(seg ...*string) error {
for i := range seg {
if seg[i] == nil || len(*seg[i]) == 0 {
return fmt.Errorf("path segment [%d] is empty", i)
}
*seg[i] = url.PathEscape(*seg[i])
}
return nil
}

View File

@ -17,6 +17,9 @@ type ListForksOptions struct {
// ListForks list a repository's forks // ListForks list a repository's forks
func (c *Client) ListForks(user string, repo string, opt ListForksOptions) ([]*Repository, *Response, error) { func (c *Client) ListForks(user string, repo string, opt ListForksOptions) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
forks := make([]*Repository, opt.PageSize) forks := make([]*Repository, opt.PageSize)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
@ -33,6 +36,9 @@ type CreateForkOption struct {
// CreateFork create a fork of a repository // CreateFork create a fork of a repository
func (c *Client) CreateFork(user, repo string, form CreateForkOption) (*Repository, *Response, error) { func (c *Client) CreateFork(user, repo string, form CreateForkOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(form) body, err := json.Marshal(form)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -19,6 +19,9 @@ type GitBlobResponse struct {
// GetBlob get the blob of a repository file // GetBlob get the blob of a repository file
func (c *Client) GetBlob(user, repo, sha string) (*GitBlobResponse, *Response, error) { func (c *Client) GetBlob(user, repo, sha string) (*GitBlobResponse, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &sha); err != nil {
return nil, nil, err
}
blob := new(GitBlobResponse) blob := new(GitBlobResponse)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/blobs/%s", user, repo, sha), nil, nil, blob) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/blobs/%s", user, repo, sha), nil, nil, blob)
return blob, resp, err return blob, resp, err

View File

@ -24,6 +24,9 @@ type ListRepoGitHooksOptions struct {
// ListRepoGitHooks list all the Git hooks of one repository // ListRepoGitHooks list all the Git hooks of one repository
func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions) ([]*GitHook, *Response, error) { func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions) ([]*GitHook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
hooks := make([]*GitHook, 0, opt.PageSize) hooks := make([]*GitHook, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
@ -32,6 +35,9 @@ func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions
// GetRepoGitHook get a Git hook of a repository // GetRepoGitHook get a Git hook of a repository
func (c *Client) GetRepoGitHook(user, repo, id string) (*GitHook, *Response, error) { func (c *Client) GetRepoGitHook(user, repo, id string) (*GitHook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
return nil, nil, err
}
h := new(GitHook) h := new(GitHook)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil, h) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil, h)
return h, resp, err return h, resp, err
@ -44,6 +50,9 @@ type EditGitHookOption struct {
// EditRepoGitHook modify one Git hook of a repository // EditRepoGitHook modify one Git hook of a repository
func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) (*Response, error) { func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
return nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@ -54,6 +63,9 @@ func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) (
// DeleteRepoGitHook delete one Git hook from a repository // DeleteRepoGitHook delete one Git hook from a repository
func (c *Client) DeleteRepoGitHook(user, repo, id string) (*Response, error) { func (c *Client) DeleteRepoGitHook(user, repo, id string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil)
return resp, err return resp, err
} }

View File

@ -3,6 +3,7 @@ module code.gitea.io/sdk/gitea
go 1.13 go 1.13
require ( require (
code.gitea.io/gitea-vet v0.2.1 // indirect
github.com/hashicorp/go-version v1.2.1 github.com/hashicorp/go-version v1.2.1
github.com/stretchr/testify v1.4.0 github.com/stretchr/testify v1.4.0
) )

View File

@ -1,7 +1,7 @@
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -9,6 +9,24 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

View File

@ -24,6 +24,28 @@ type Hook struct {
Created time.Time `json:"created_at"` Created time.Time `json:"created_at"`
} }
// HookType represent all webhook types gitea currently offer
type HookType string
const (
// HookTypeDingtalk webhook that dingtalk understand
HookTypeDingtalk HookType = "dingtalk"
// HookTypeDiscord webhook that discord understand
HookTypeDiscord HookType = "discord"
// HookTypeGitea webhook that gitea understand
HookTypeGitea HookType = "gitea"
// HookTypeGogs webhook that gogs understand
HookTypeGogs HookType = "gogs"
// HookTypeMsteams webhook that msteams understand
HookTypeMsteams HookType = "msteams"
// HookTypeSlack webhook that slack understand
HookTypeSlack HookType = "slack"
// HookTypeTelegram webhook that telegram understand
HookTypeTelegram HookType = "telegram"
// HookTypeFeishu webhook that feishu understand
HookTypeFeishu HookType = "feishu"
)
// ListHooksOptions options for listing hooks // ListHooksOptions options for listing hooks
type ListHooksOptions struct { type ListHooksOptions struct {
ListOptions ListOptions
@ -31,6 +53,9 @@ type ListHooksOptions struct {
// ListOrgHooks list all the hooks of one organization // ListOrgHooks list all the hooks of one organization
func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, *Response, error) { func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
hooks := make([]*Hook, 0, opt.PageSize) hooks := make([]*Hook, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks?%s", org, opt.getURLQuery().Encode()), nil, nil, &hooks) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks?%s", org, opt.getURLQuery().Encode()), nil, nil, &hooks)
@ -39,6 +64,9 @@ func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, *Respo
// ListRepoHooks list all the hooks of one repository // ListRepoHooks list all the hooks of one repository
func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook, *Response, error) { func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
hooks := make([]*Hook, 0, opt.PageSize) hooks := make([]*Hook, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
@ -47,6 +75,9 @@ func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook
// GetOrgHook get a hook of an organization // GetOrgHook get a hook of an organization
func (c *Client) GetOrgHook(org string, id int64) (*Hook, *Response, error) { func (c *Client) GetOrgHook(org string, id int64) (*Hook, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
h := new(Hook) h := new(Hook)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil, h) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil, h)
return h, resp, err return h, resp, err
@ -54,6 +85,9 @@ func (c *Client) GetOrgHook(org string, id int64) (*Hook, *Response, error) {
// GetRepoHook get a hook of a repository // GetRepoHook get a hook of a repository
func (c *Client) GetRepoHook(user, repo string, id int64) (*Hook, *Response, error) { func (c *Client) GetRepoHook(user, repo string, id int64) (*Hook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
h := new(Hook) h := new(Hook)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil, h) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil, h)
return h, resp, err return h, resp, err
@ -61,7 +95,7 @@ func (c *Client) GetRepoHook(user, repo string, id int64) (*Hook, *Response, err
// CreateHookOption options when create a hook // CreateHookOption options when create a hook
type CreateHookOption struct { type CreateHookOption struct {
Type string `json:"type"` Type HookType `json:"type"`
Config map[string]string `json:"config"` Config map[string]string `json:"config"`
Events []string `json:"events"` Events []string `json:"events"`
BranchFilter string `json:"branch_filter"` BranchFilter string `json:"branch_filter"`
@ -78,6 +112,9 @@ func (opt CreateHookOption) Validate() error {
// CreateOrgHook create one hook for an organization, with options // CreateOrgHook create one hook for an organization, with options
func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, *Response, error) { func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -92,6 +129,9 @@ func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, *Respon
// CreateRepoHook create one hook for a repository, with options // CreateRepoHook create one hook for a repository, with options
func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook, *Response, error) { func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -111,6 +151,9 @@ type EditHookOption struct {
// EditOrgHook modify one hook of an organization, with hook id and options // EditOrgHook modify one hook of an organization, with hook id and options
func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) (*Response, error) { func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) (*Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@ -121,6 +164,9 @@ func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) (*Respons
// EditRepoHook modify one hook of a repository, with hook id and options // EditRepoHook modify one hook of a repository, with hook id and options
func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) (*Response, error) { func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@ -131,12 +177,18 @@ func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) (
// DeleteOrgHook delete one hook from an organization, with hook id // DeleteOrgHook delete one hook from an organization, with hook id
func (c *Client) DeleteOrgHook(org string, id int64) (*Response, error) { func (c *Client) DeleteOrgHook(org string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil)
return resp, err return resp, err
} }
// DeleteRepoHook delete one hook from a repository, with hook id // DeleteRepoHook delete one hook from a repository, with hook id
func (c *Client) DeleteRepoHook(user, repo string, id int64) (*Response, error) { func (c *Client) DeleteRepoHook(user, repo string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil)
return resp, err return resp, err
} }

View File

@ -42,10 +42,7 @@ type Issue struct {
Ref string `json:"ref"` Ref string `json:"ref"`
Labels []*Label `json:"labels"` Labels []*Label `json:"labels"`
Milestone *Milestone `json:"milestone"` Milestone *Milestone `json:"milestone"`
// deprecated Assignees []*User `json:"assignees"`
// TODO: rm on sdk 0.15.0
Assignee *User `json:"assignee"`
Assignees []*User `json:"assignees"`
// Whether the issue is open or closed // Whether the issue is open or closed
State StateType `json:"state"` State StateType `json:"state"`
IsLocked bool `json:"is_locked"` IsLocked bool `json:"is_locked"`
@ -66,6 +63,14 @@ type ListIssueOption struct {
Labels []string Labels []string
Milestones []string Milestones []string
KeyWord string KeyWord string
Since time.Time
Before time.Time
// filter by created by username
CreatedBy string
// filter by assigned to username
AssignedBy string
// filter by username mentioned
MentionedBy string
} }
// StateType issue state type // StateType issue state type
@ -114,6 +119,23 @@ func (opt *ListIssueOption) QueryEncode() string {
query.Add("milestones", strings.Join(opt.Milestones, ",")) query.Add("milestones", strings.Join(opt.Milestones, ","))
} }
if !opt.Since.IsZero() {
query.Add("since", opt.Since.Format(time.RFC3339))
}
if !opt.Before.IsZero() {
query.Add("before", opt.Before.Format(time.RFC3339))
}
if len(opt.CreatedBy) > 0 {
query.Add("created_by", opt.CreatedBy)
}
if len(opt.AssignedBy) > 0 {
query.Add("assigned_by", opt.AssignedBy)
}
if len(opt.MentionedBy) > 0 {
query.Add("mentioned_by", opt.MentionedBy)
}
return query.Encode() return query.Encode()
} }
@ -140,6 +162,9 @@ func (c *Client) ListIssues(opt ListIssueOption) ([]*Issue, *Response, error) {
// ListRepoIssues returns all issues for a given repository // ListRepoIssues returns all issues for a given repository
func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Issue, *Response, error) { func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Issue, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
issues := make([]*Issue, 0, opt.PageSize) issues := make([]*Issue, 0, opt.PageSize)
@ -161,6 +186,9 @@ func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Iss
// GetIssue returns a single issue for a given repository // GetIssue returns a single issue for a given repository
func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, *Response, error) { func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
issue := new(Issue) issue := new(Issue)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue)
if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil && issue.Repository != nil { if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil && issue.Repository != nil {
@ -172,12 +200,9 @@ func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, *Response, e
// CreateIssueOption options to create one issue // CreateIssueOption options to create one issue
type CreateIssueOption struct { type CreateIssueOption struct {
Title string `json:"title"` Title string `json:"title"`
Body string `json:"body"` Body string `json:"body"`
Ref string `json:"ref"` Ref string `json:"ref"`
// deprecated
// TODO: rm on sdk 0.15.0
Assignee string `json:"assignee"`
Assignees []string `json:"assignees"` Assignees []string `json:"assignees"`
Deadline *time.Time `json:"due_date"` Deadline *time.Time `json:"due_date"`
// milestone id // milestone id
@ -197,6 +222,9 @@ func (opt CreateIssueOption) Validate() error {
// CreateIssue create a new issue for a given repository // CreateIssue create a new issue for a given repository
func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue, *Response, error) { func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -213,12 +241,9 @@ func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue,
// EditIssueOption options for editing an issue // EditIssueOption options for editing an issue
type EditIssueOption struct { type EditIssueOption struct {
Title string `json:"title"` Title string `json:"title"`
Body *string `json:"body"` Body *string `json:"body"`
Ref *string `json:"ref"` Ref *string `json:"ref"`
// deprecated
// TODO: rm on sdk 0.15.0
Assignee *string `json:"assignee"`
Assignees []string `json:"assignees"` Assignees []string `json:"assignees"`
Milestone *int64 `json:"milestone"` Milestone *int64 `json:"milestone"`
State *StateType `json:"state"` State *StateType `json:"state"`
@ -236,6 +261,9 @@ func (opt EditIssueOption) Validate() error {
// EditIssue modify an existing issue for a given repository // EditIssue modify an existing issue for a given repository
func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption) (*Issue, *Response, error) { func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption) (*Issue, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -47,6 +47,9 @@ func (opt *ListIssueCommentOptions) QueryEncode() string {
// ListIssueComments list comments on an issue. // ListIssueComments list comments on an issue.
func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssueCommentOptions) ([]*Comment, *Response, error) { func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssueCommentOptions) ([]*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
@ -57,6 +60,9 @@ func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssu
// ListRepoIssueComments list comments for a given repo. // ListRepoIssueComments list comments for a given repo.
func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentOptions) ([]*Comment, *Response, error) { func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentOptions) ([]*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
@ -67,6 +73,9 @@ func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentO
// GetIssueComment get a comment for a given repo by id. // GetIssueComment get a comment for a given repo by id.
func (c *Client) GetIssueComment(owner, repo string, id int64) (*Comment, *Response, error) { func (c *Client) GetIssueComment(owner, repo string, id int64) (*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
comment := new(Comment) comment := new(Comment)
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return comment, nil, err return comment, nil, err
@ -90,6 +99,9 @@ func (opt CreateIssueCommentOption) Validate() error {
// CreateIssueComment create comment on an issue. // CreateIssueComment create comment on an issue.
func (c *Client) CreateIssueComment(owner, repo string, index int64, opt CreateIssueCommentOption) (*Comment, *Response, error) { func (c *Client) CreateIssueComment(owner, repo string, index int64, opt CreateIssueCommentOption) (*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -117,6 +129,9 @@ func (opt EditIssueCommentOption) Validate() error {
// EditIssueComment edits an issue comment. // EditIssueComment edits an issue comment.
func (c *Client) EditIssueComment(owner, repo string, commentID int64, opt EditIssueCommentOption) (*Comment, *Response, error) { func (c *Client) EditIssueComment(owner, repo string, commentID int64, opt EditIssueCommentOption) (*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -131,6 +146,9 @@ func (c *Client) EditIssueComment(owner, repo string, commentID int64, opt EditI
// DeleteIssueComment deletes an issue comment. // DeleteIssueComment deletes an issue comment.
func (c *Client) DeleteIssueComment(owner, repo string, commentID int64) (*Response, error) { func (c *Client) DeleteIssueComment(owner, repo string, commentID int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, commentID), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, commentID), nil, nil)
return resp, err return resp, err
} }

View File

@ -29,6 +29,9 @@ type ListLabelsOptions struct {
// ListRepoLabels list labels of one repository // ListRepoLabels list labels of one repository
func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*Label, *Response, error) { func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
labels := make([]*Label, 0, opt.PageSize) labels := make([]*Label, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels?%s", owner, repo, opt.getURLQuery().Encode()), nil, nil, &labels) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels?%s", owner, repo, opt.getURLQuery().Encode()), nil, nil, &labels)
@ -37,6 +40,9 @@ func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*L
// GetRepoLabel get one label of repository by repo it // GetRepoLabel get one label of repository by repo it
func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, *Response, error) { func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
label := new(Label) label := new(Label)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil, label) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil, label)
return label, resp, err return label, resp, err
@ -67,6 +73,9 @@ func (opt CreateLabelOption) Validate() error {
// CreateLabel create one label of repository // CreateLabel create one label of repository
func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, *Response, error) { func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -114,6 +123,9 @@ func (opt EditLabelOption) Validate() error {
// EditLabel modify one label with options // EditLabel modify one label with options
func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*Label, *Response, error) { func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -128,12 +140,18 @@ func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*
// DeleteLabel delete one label of repository by id // DeleteLabel delete one label of repository by id
func (c *Client) DeleteLabel(owner, repo string, id int64) (*Response, error) { func (c *Client) DeleteLabel(owner, repo string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil)
return resp, err return resp, err
} }
// GetIssueLabels get labels of one issue via issue id // GetIssueLabels get labels of one issue via issue id
func (c *Client) GetIssueLabels(owner, repo string, index int64, opts ListLabelsOptions) ([]*Label, *Response, error) { func (c *Client) GetIssueLabels(owner, repo string, index int64, opts ListLabelsOptions) ([]*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
labels := make([]*Label, 0, 5) labels := make([]*Label, 0, 5)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels?%s", owner, repo, index, opts.getURLQuery().Encode()), nil, nil, &labels) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels?%s", owner, repo, index, opts.getURLQuery().Encode()), nil, nil, &labels)
return labels, resp, err return labels, resp, err
@ -147,6 +165,9 @@ type IssueLabelsOption struct {
// AddIssueLabels add one or more labels to one issue // AddIssueLabels add one or more labels to one issue
func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) { func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -158,6 +179,9 @@ func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabels
// ReplaceIssueLabels replace old labels of issue with new labels // ReplaceIssueLabels replace old labels of issue with new labels
func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) { func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -170,12 +194,18 @@ func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLa
// DeleteIssueLabel delete one label of one issue by issue id and label id // DeleteIssueLabel delete one label of one issue by issue id and label id
// TODO: maybe we need delete by label name and issue id // TODO: maybe we need delete by label name and issue id
func (c *Client) DeleteIssueLabel(owner, repo string, index, label int64) (*Response, error) { func (c *Client) DeleteIssueLabel(owner, repo string, index, label int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, label), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, label), nil, nil)
return resp, err return resp, err
} }
// ClearIssueLabels delete all the labels of one issue. // ClearIssueLabels delete all the labels of one issue.
func (c *Client) ClearIssueLabels(owner, repo string, index int64) (*Response, error) { func (c *Client) ClearIssueLabels(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil)
return resp, err return resp, err
} }

View File

@ -49,6 +49,9 @@ func (opt *ListMilestoneOption) QueryEncode() string {
// ListRepoMilestones list all the milestones of one repository // ListRepoMilestones list all the milestones of one repository
func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption) ([]*Milestone, *Response, error) { func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption) ([]*Milestone, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
milestones := make([]*Milestone, 0, opt.PageSize) milestones := make([]*Milestone, 0, opt.PageSize)
@ -60,6 +63,9 @@ func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption)
// GetMilestone get one milestone by repo name and milestone id // GetMilestone get one milestone by repo name and milestone id
func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Response, error) { func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
milestone := new(Milestone) milestone := new(Milestone)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil, milestone) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil, milestone)
return milestone, resp, err return milestone, resp, err
@ -72,6 +78,9 @@ func (c *Client) GetMilestoneByName(owner, repo string, name string) (*Milestone
m, resp, err := c.resolveMilestoneByName(owner, repo, name) m, resp, err := c.resolveMilestoneByName(owner, repo, name)
return m, resp, err return m, resp, err
} }
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, nil, err
}
milestone := new(Milestone) milestone := new(Milestone)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil, milestone) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil, milestone)
return milestone, resp, err return milestone, resp, err
@ -95,6 +104,9 @@ func (opt CreateMilestoneOption) Validate() error {
// CreateMilestone create one milestone with options // CreateMilestone create one milestone with options
func (c *Client) CreateMilestone(owner, repo string, opt CreateMilestoneOption) (*Milestone, *Response, error) { func (c *Client) CreateMilestone(owner, repo string, opt CreateMilestoneOption) (*Milestone, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -135,6 +147,9 @@ func (opt EditMilestoneOption) Validate() error {
// EditMilestone modify milestone with options // EditMilestone modify milestone with options
func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOption) (*Milestone, *Response, error) { func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOption) (*Milestone, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -157,6 +172,9 @@ func (c *Client) EditMilestoneByName(owner, repo string, name string, opt EditMi
} }
return c.EditMilestone(owner, repo, m.ID, opt) return c.EditMilestone(owner, repo, m.ID, opt)
} }
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -171,6 +189,9 @@ func (c *Client) EditMilestoneByName(owner, repo string, name string, opt EditMi
// DeleteMilestone delete one milestone by id // DeleteMilestone delete one milestone by id
func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error) { func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil)
return resp, err return resp, err
} }
@ -185,6 +206,9 @@ func (c *Client) DeleteMilestoneByName(owner, repo string, name string) (*Respon
} }
return c.DeleteMilestone(owner, repo, m.ID) return c.DeleteMilestone(owner, repo, m.ID)
} }
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil)
return resp, err return resp, err
} }

View File

@ -20,6 +20,9 @@ type Reaction struct {
// GetIssueReactions get a list reactions of an issue // GetIssueReactions get a list reactions of an issue
func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction, *Response, error) { func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
reactions := make([]*Reaction, 0, 10) reactions := make([]*Reaction, 0, 10)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index), nil, nil, &reactions) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index), nil, nil, &reactions)
return reactions, resp, err return reactions, resp, err
@ -27,6 +30,9 @@ func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction
// GetIssueCommentReactions get a list of reactions from a comment of an issue // GetIssueCommentReactions get a list of reactions from a comment of an issue
func (c *Client) GetIssueCommentReactions(owner, repo string, commentID int64) ([]*Reaction, *Response, error) { func (c *Client) GetIssueCommentReactions(owner, repo string, commentID int64) ([]*Reaction, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
reactions := make([]*Reaction, 0, 10) reactions := make([]*Reaction, 0, 10)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID), nil, nil, &reactions) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID), nil, nil, &reactions)
return reactions, resp, err return reactions, resp, err
@ -39,6 +45,9 @@ type editReactionOption struct {
// PostIssueReaction add a reaction to an issue // PostIssueReaction add a reaction to an issue
func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction string) (*Reaction, *Response, error) { func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction string) (*Reaction, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
reactionResponse := new(Reaction) reactionResponse := new(Reaction)
body, err := json.Marshal(&editReactionOption{Reaction: reaction}) body, err := json.Marshal(&editReactionOption{Reaction: reaction})
if err != nil { if err != nil {
@ -52,6 +61,9 @@ func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction str
// DeleteIssueReaction remove a reaction from an issue // DeleteIssueReaction remove a reaction from an issue
func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction string) (*Response, error) { func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
body, err := json.Marshal(&editReactionOption{Reaction: reaction}) body, err := json.Marshal(&editReactionOption{Reaction: reaction})
if err != nil { if err != nil {
return nil, err return nil, err
@ -62,6 +74,9 @@ func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction s
// PostIssueCommentReaction add a reaction to a comment of an issue // PostIssueCommentReaction add a reaction to a comment of an issue
func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Reaction, *Response, error) { func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Reaction, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
reactionResponse := new(Reaction) reactionResponse := new(Reaction)
body, err := json.Marshal(&editReactionOption{Reaction: reaction}) body, err := json.Marshal(&editReactionOption{Reaction: reaction})
if err != nil { if err != nil {
@ -75,6 +90,9 @@ func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, r
// DeleteIssueCommentReaction remove a reaction from a comment of an issue // DeleteIssueCommentReaction remove a reaction from a comment of an issue
func (c *Client) DeleteIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Response, error) { func (c *Client) DeleteIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
body, err := json.Marshal(&editReactionOption{Reaction: reaction}) body, err := json.Marshal(&editReactionOption{Reaction: reaction})
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -29,6 +29,9 @@ func (c *Client) GetMyStopwatches() ([]*StopWatch, *Response, error) {
// DeleteIssueStopwatch delete / cancel a specific stopwatch // DeleteIssueStopwatch delete / cancel a specific stopwatch
func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) (*Response, error) { func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/delete", owner, repo, index), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/delete", owner, repo, index), nil, nil)
return resp, err return resp, err
} }
@ -36,6 +39,9 @@ func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) (*Respons
// StartIssueStopWatch starts a stopwatch for an existing issue for a given // StartIssueStopWatch starts a stopwatch for an existing issue for a given
// repository // repository
func (c *Client) StartIssueStopWatch(owner, repo string, index int64) (*Response, error) { func (c *Client) StartIssueStopWatch(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index), nil, nil) _, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index), nil, nil)
return resp, err return resp, err
} }
@ -43,6 +49,9 @@ func (c *Client) StartIssueStopWatch(owner, repo string, index int64) (*Response
// StopIssueStopWatch stops an existing stopwatch for an issue in a given // StopIssueStopWatch stops an existing stopwatch for an issue in a given
// repository // repository
func (c *Client) StopIssueStopWatch(owner, repo string, index int64) (*Response, error) { func (c *Client) StopIssueStopWatch(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index), nil, nil) _, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index), nil, nil)
return resp, err return resp, err
} }

View File

@ -11,6 +11,9 @@ import (
// GetIssueSubscribers get list of users who subscribed on an issue // GetIssueSubscribers get list of users who subscribed on an issue
func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User, *Response, error) { func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
subscribers := make([]*User, 0, 10) subscribers := make([]*User, 0, 10)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions", owner, repo, index), nil, nil, &subscribers) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions", owner, repo, index), nil, nil, &subscribers)
return subscribers, resp, err return subscribers, resp, err
@ -18,6 +21,9 @@ func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User,
// AddIssueSubscription Subscribe user to issue // AddIssueSubscription Subscribe user to issue
func (c *Client) AddIssueSubscription(owner, repo string, index int64, user string) (*Response, error) { func (c *Client) AddIssueSubscription(owner, repo string, index int64, user string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &user); err != nil {
return nil, err
}
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil) status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
if err != nil { if err != nil {
return resp, err return resp, err
@ -33,6 +39,9 @@ func (c *Client) AddIssueSubscription(owner, repo string, index int64, user stri
// DeleteIssueSubscription unsubscribe user from issue // DeleteIssueSubscription unsubscribe user from issue
func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user string) (*Response, error) { func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &user); err != nil {
return nil, err
}
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil) status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
if err != nil { if err != nil {
return resp, err return resp, err
@ -48,6 +57,9 @@ func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user s
// CheckIssueSubscription check if current user is subscribed to an issue // CheckIssueSubscription check if current user is subscribed to an issue
func (c *Client) CheckIssueSubscription(owner, repo string, index int64) (*WatchInfo, *Response, error) { func (c *Client) CheckIssueSubscription(owner, repo string, index int64) (*WatchInfo, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -55,6 +55,9 @@ func (opt *ListTrackedTimesOptions) QueryEncode() string {
// ListRepoTrackedTimes list tracked times of a repository // ListRepoTrackedTimes list tracked times of a repository
func (c *Client) ListRepoTrackedTimes(owner, repo string, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) { func (c *Client) ListRepoTrackedTimes(owner, repo string, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/times", owner, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/times", owner, repo))
opt.setDefaults() opt.setDefaults()
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
@ -90,6 +93,9 @@ func (opt AddTimeOption) Validate() error {
// AddTime adds time to issue with the given index // AddTime adds time to issue with the given index
func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*TrackedTime, *Response, error) { func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*TrackedTime, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -106,6 +112,9 @@ func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*T
// ListIssueTrackedTimes list tracked times of a single issue for a given repository // ListIssueTrackedTimes list tracked times of a single issue for a given repository
func (c *Client) ListIssueTrackedTimes(owner, repo string, index int64, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) { func (c *Client) ListIssueTrackedTimes(owner, repo string, index int64, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index))
opt.setDefaults() opt.setDefaults()
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
@ -116,12 +125,18 @@ func (c *Client) ListIssueTrackedTimes(owner, repo string, index int64, opt List
// ResetIssueTime reset tracked time of a single issue for a given repository // ResetIssueTime reset tracked time of a single issue for a given repository
func (c *Client) ResetIssueTime(owner, repo string, index int64) (*Response, error) { func (c *Client) ResetIssueTime(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), jsonHeader, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), jsonHeader, nil)
return resp, err return resp, err
} }
// DeleteTime delete a specific tracked time by id of a single issue for a given repository // DeleteTime delete a specific tracked time by id of a single issue for a given repository
func (c *Client) DeleteTime(owner, repo string, index, timeID int64) (*Response, error) { func (c *Client) DeleteTime(owner, repo string, index, timeID int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times/%d", owner, repo, index, timeID), jsonHeader, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times/%d", owner, repo, index, timeID), jsonHeader, nil)
return resp, err return resp, err
} }

View File

@ -26,8 +26,13 @@ func (o ListOptions) getURLQuery() url.Values {
return query return query
} }
func (o ListOptions) setDefaults() { // setDefaults set default pagination options if none or wrong are set
if o.Page < 1 { // if you set -1 as page it will set all to 0
func (o *ListOptions) setDefaults() {
if o.Page < 0 {
o.Page, o.PageSize = 0, 0
return
} else if o.Page == 0 {
o.Page = 1 o.Page = 1
} }

View File

@ -29,11 +29,11 @@ type NotificationThread struct {
// NotificationSubject contains the notification subject (Issue/Pull/Commit) // NotificationSubject contains the notification subject (Issue/Pull/Commit)
type NotificationSubject struct { type NotificationSubject struct {
Title string `json:"title"` Title string `json:"title"`
URL string `json:"url"` URL string `json:"url"`
LatestCommentURL string `json:"latest_comment_url"` LatestCommentURL string `json:"latest_comment_url"`
Type string `json:"type"` Type NotifySubjectType `json:"type"`
State StateType `json:"state"` State NotifySubjectState `json:"state"`
} }
// NotifyStatus notification status type // NotifyStatus notification status type
@ -48,12 +48,39 @@ const (
NotifyStatusPinned NotifyStatus = "pinned" NotifyStatusPinned NotifyStatus = "pinned"
) )
// NotifySubjectType represent type of notification subject
type NotifySubjectType string
const (
// NotifySubjectIssue an issue is subject of an notification
NotifySubjectIssue NotifySubjectType = "Issue"
// NotifySubjectPull an pull is subject of an notification
NotifySubjectPull NotifySubjectType = "Pull"
// NotifySubjectCommit an commit is subject of an notification
NotifySubjectCommit NotifySubjectType = "Commit"
// NotifySubjectRepository an repository is subject of an notification
NotifySubjectRepository NotifySubjectType = "Repository"
)
// NotifySubjectState reflect state of notification subject
type NotifySubjectState string
const (
// NotifySubjectOpen if subject is a pull/issue and is open at the moment
NotifySubjectOpen NotifySubjectState = "open"
// NotifySubjectClosed if subject is a pull/issue and is closed at the moment
NotifySubjectClosed NotifySubjectState = "closed"
// NotifySubjectMerged if subject is a pull and got merged
NotifySubjectMerged NotifySubjectState = "merged"
)
// ListNotificationOptions represents the filter options // ListNotificationOptions represents the filter options
type ListNotificationOptions struct { type ListNotificationOptions struct {
ListOptions ListOptions
Since time.Time Since time.Time
Before time.Time Before time.Time
Status []NotifyStatus Status []NotifyStatus
SubjectTypes []NotifySubjectType
} }
// MarkNotificationOptions represents the filter & modify options // MarkNotificationOptions represents the filter & modify options
@ -75,6 +102,9 @@ func (opt *ListNotificationOptions) QueryEncode() string {
for _, s := range opt.Status { for _, s := range opt.Status {
query.Add("status-types", string(s)) query.Add("status-types", string(s))
} }
for _, s := range opt.SubjectTypes {
query.Add("subject-type", string(s))
}
return query.Encode() return query.Encode()
} }
@ -176,14 +206,17 @@ func (c *Client) ReadNotifications(opt MarkNotificationOptions) (*Response, erro
} }
// ListRepoNotifications list users's notification threads on a specific repo // ListRepoNotifications list users's notification threads on a specific repo
func (c *Client) ListRepoNotifications(owner, reponame string, opt ListNotificationOptions) ([]*NotificationThread, *Response, error) { func (c *Client) ListRepoNotifications(owner, repo string, opt ListNotificationOptions) ([]*NotificationThread, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return nil, nil, err return nil, nil, err
} }
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, reponame)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, repo))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
threads := make([]*NotificationThread, 0, 10) threads := make([]*NotificationThread, 0, 10)
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &threads) resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &threads)
@ -191,14 +224,17 @@ func (c *Client) ListRepoNotifications(owner, reponame string, opt ListNotificat
} }
// ReadRepoNotifications mark notification threads as read on a specific repo // ReadRepoNotifications mark notification threads as read on a specific repo
func (c *Client) ReadRepoNotifications(owner, reponame string, opt MarkNotificationOptions) (*Response, error) { func (c *Client) ReadRepoNotifications(owner, repo string, opt MarkNotificationOptions) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, err return nil, err
} }
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return nil, err return nil, err
} }
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, reponame)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, repo))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
_, resp, err := c.getResponse("PUT", link.String(), nil, nil) _, resp, err := c.getResponse("PUT", link.String(), nil, nil)
return resp, err return resp, err

View File

@ -52,6 +52,9 @@ func (c *Client) ListMyOrgs(opt ListOrgsOptions) ([]*Organization, *Response, er
// ListUserOrgs list all of some user's organizations // ListUserOrgs list all of some user's organizations
func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization, *Response, error) { func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
orgs := make([]*Organization, 0, opt.PageSize) orgs := make([]*Organization, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs?%s", user, opt.getURLQuery().Encode()), nil, nil, &orgs) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs?%s", user, opt.getURLQuery().Encode()), nil, nil, &orgs)
@ -60,6 +63,9 @@ func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization
// GetOrg get one organization by name // GetOrg get one organization by name
func (c *Client) GetOrg(orgname string) (*Organization, *Response, error) { func (c *Client) GetOrg(orgname string) (*Organization, *Response, error) {
if err := escapeValidatePathSegments(&orgname); err != nil {
return nil, nil, err
}
org := new(Organization) org := new(Organization)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s", orgname), nil, nil, org) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s", orgname), nil, nil, org)
return org, resp, err return org, resp, err
@ -67,12 +73,13 @@ func (c *Client) GetOrg(orgname string) (*Organization, *Response, error) {
// CreateOrgOption options for creating an organization // CreateOrgOption options for creating an organization
type CreateOrgOption struct { type CreateOrgOption struct {
Name string `json:"username"` Name string `json:"username"`
FullName string `json:"full_name"` FullName string `json:"full_name"`
Description string `json:"description"` Description string `json:"description"`
Website string `json:"website"` Website string `json:"website"`
Location string `json:"location"` Location string `json:"location"`
Visibility VisibleType `json:"visibility"` Visibility VisibleType `json:"visibility"`
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
} }
// checkVisibilityOpt check if mode exist // checkVisibilityOpt check if mode exist
@ -124,6 +131,9 @@ func (opt EditOrgOption) Validate() error {
// EditOrg modify one organization via options // EditOrg modify one organization via options
func (c *Client) EditOrg(orgname string, opt EditOrgOption) (*Response, error) { func (c *Client) EditOrg(orgname string, opt EditOrgOption) (*Response, error) {
if err := escapeValidatePathSegments(&orgname); err != nil {
return nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, err return nil, err
} }
@ -137,6 +147,9 @@ func (c *Client) EditOrg(orgname string, opt EditOrgOption) (*Response, error) {
// DeleteOrg deletes an organization // DeleteOrg deletes an organization
func (c *Client) DeleteOrg(orgname string) (*Response, error) { func (c *Client) DeleteOrg(orgname string) (*Response, error) {
if err := escapeValidatePathSegments(&orgname); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, nil)
return resp, err return resp, err
} }

View File

@ -12,7 +12,10 @@ import (
// DeleteOrgMembership remove a member from an organization // DeleteOrgMembership remove a member from an organization
func (c *Client) DeleteOrgMembership(org, user string) (*Response, error) { func (c *Client) DeleteOrgMembership(org, user string) (*Response, error) {
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) if err := escapeValidatePathSegments(&org, &user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/members/%s", org, user), nil, nil)
return resp, err return resp, err
} }
@ -23,10 +26,13 @@ type ListOrgMembershipOption struct {
// ListOrgMembership list an organization's members // ListOrgMembership list an organization's members
func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) { func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
users := make([]*User, 0, opt.PageSize) users := make([]*User, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/members", url.PathEscape(org))) link, _ := url.Parse(fmt.Sprintf("/orgs/%s/members", org))
link.RawQuery = opt.getURLQuery().Encode() link.RawQuery = opt.getURLQuery().Encode()
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users) resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
return users, resp, err return users, resp, err
@ -34,10 +40,13 @@ func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*
// ListPublicOrgMembership list an organization's members // ListPublicOrgMembership list an organization's members
func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) { func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
users := make([]*User, 0, opt.PageSize) users := make([]*User, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/public_members", url.PathEscape(org))) link, _ := url.Parse(fmt.Sprintf("/orgs/%s/public_members", org))
link.RawQuery = opt.getURLQuery().Encode() link.RawQuery = opt.getURLQuery().Encode()
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users) resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
return users, resp, err return users, resp, err
@ -45,7 +54,10 @@ func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption
// CheckOrgMembership Check if a user is a member of an organization // CheckOrgMembership Check if a user is a member of an organization
func (c *Client) CheckOrgMembership(org, user string) (bool, *Response, error) { func (c *Client) CheckOrgMembership(org, user string) (bool, *Response, error) {
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) if err := escapeValidatePathSegments(&org, &user); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/members/%s", org, user), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
} }
@ -61,7 +73,10 @@ func (c *Client) CheckOrgMembership(org, user string) (bool, *Response, error) {
// CheckPublicOrgMembership Check if a user is a member of an organization // CheckPublicOrgMembership Check if a user is a member of an organization
func (c *Client) CheckPublicOrgMembership(org, user string) (bool, *Response, error) { func (c *Client) CheckPublicOrgMembership(org, user string) (bool, *Response, error) {
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) if err := escapeValidatePathSegments(&org, &user); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
} }
@ -77,15 +92,18 @@ func (c *Client) CheckPublicOrgMembership(org, user string) (bool, *Response, er
// SetPublicOrgMembership publicize/conceal a user's membership // SetPublicOrgMembership publicize/conceal a user's membership
func (c *Client) SetPublicOrgMembership(org, user string, visible bool) (*Response, error) { func (c *Client) SetPublicOrgMembership(org, user string, visible bool) (*Response, error) {
if err := escapeValidatePathSegments(&org, &user); err != nil {
return nil, err
}
var ( var (
status int status int
err error err error
resp *Response resp *Response
) )
if visible { if visible {
status, resp, err = c.getStatusCode("PUT", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) status, resp, err = c.getStatusCode("PUT", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
} else { } else {
status, resp, err = c.getStatusCode("DELETE", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) status, resp, err = c.getStatusCode("DELETE", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
} }
if err != nil { if err != nil {
return resp, err return resp, err

View File

@ -12,17 +12,38 @@ import (
// Team represents a team in an organization // Team represents a team in an organization
type Team struct { type Team struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
Organization *Organization `json:"organization"` Organization *Organization `json:"organization"`
Permission AccessMode `json:"permission"` Permission AccessMode `json:"permission"`
CanCreateOrgRepo bool `json:"can_create_org_repo"` CanCreateOrgRepo bool `json:"can_create_org_repo"`
IncludesAllRepositories bool `json:"includes_all_repositories"` IncludesAllRepositories bool `json:"includes_all_repositories"`
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] Units []RepoUnitType `json:"units"`
Units []string `json:"units"`
} }
// RepoUnitType represent all unit types of a repo gitea currently offer
type RepoUnitType string
const (
// RepoUnitCode represent file view of a repository
RepoUnitCode RepoUnitType = "repo.code"
// RepoUnitIssues represent issues of a repository
RepoUnitIssues RepoUnitType = "repo.issues"
// RepoUnitPulls represent pulls of a repository
RepoUnitPulls RepoUnitType = "repo.pulls"
// RepoUnitExtIssues represent external issues of a repository
RepoUnitExtIssues RepoUnitType = "repo.ext_issues"
// RepoUnitWiki represent wiki of a repository
RepoUnitWiki RepoUnitType = "repo.wiki"
// RepoUnitExtWiki represent external wiki of a repository
RepoUnitExtWiki RepoUnitType = "repo.ext_wiki"
// RepoUnitReleases represent releases of a repository
RepoUnitReleases RepoUnitType = "repo.releases"
// RepoUnitProjects represent projects of a repository
RepoUnitProjects RepoUnitType = "repo.projects"
)
// ListTeamsOptions options for listing teams // ListTeamsOptions options for listing teams
type ListTeamsOptions struct { type ListTeamsOptions struct {
ListOptions ListOptions
@ -30,6 +51,9 @@ type ListTeamsOptions struct {
// ListOrgTeams lists all teams of an organization // ListOrgTeams lists all teams of an organization
func (c *Client) ListOrgTeams(org string, opt ListTeamsOptions) ([]*Team, *Response, error) { func (c *Client) ListOrgTeams(org string, opt ListTeamsOptions) ([]*Team, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
teams := make([]*Team, 0, opt.PageSize) teams := make([]*Team, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams?%s", org, opt.getURLQuery().Encode()), nil, nil, &teams) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams?%s", org, opt.getURLQuery().Encode()), nil, nil, &teams)
@ -53,13 +77,12 @@ func (c *Client) GetTeam(id int64) (*Team, *Response, error) {
// CreateTeamOption options for creating a team // CreateTeamOption options for creating a team
type CreateTeamOption struct { type CreateTeamOption struct {
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
Permission AccessMode `json:"permission"` Permission AccessMode `json:"permission"`
CanCreateOrgRepo bool `json:"can_create_org_repo"` CanCreateOrgRepo bool `json:"can_create_org_repo"`
IncludesAllRepositories bool `json:"includes_all_repositories"` IncludesAllRepositories bool `json:"includes_all_repositories"`
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] Units []RepoUnitType `json:"units"`
Units []string `json:"units"`
} }
// Validate the CreateTeamOption struct // Validate the CreateTeamOption struct
@ -83,6 +106,9 @@ func (opt CreateTeamOption) Validate() error {
// CreateTeam creates a team for an organization // CreateTeam creates a team for an organization
func (c *Client) CreateTeam(org string, opt CreateTeamOption) (*Team, *Response, error) { func (c *Client) CreateTeam(org string, opt CreateTeamOption) (*Team, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -97,13 +123,12 @@ func (c *Client) CreateTeam(org string, opt CreateTeamOption) (*Team, *Response,
// EditTeamOption options for editing a team // EditTeamOption options for editing a team
type EditTeamOption struct { type EditTeamOption struct {
Name string `json:"name"` Name string `json:"name"`
Description *string `json:"description"` Description *string `json:"description"`
Permission AccessMode `json:"permission"` Permission AccessMode `json:"permission"`
CanCreateOrgRepo *bool `json:"can_create_org_repo"` CanCreateOrgRepo *bool `json:"can_create_org_repo"`
IncludesAllRepositories *bool `json:"includes_all_repositories"` IncludesAllRepositories *bool `json:"includes_all_repositories"`
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"] Units []RepoUnitType `json:"units"`
Units []string `json:"units"`
} }
// Validate the EditTeamOption struct // Validate the EditTeamOption struct
@ -159,6 +184,9 @@ func (c *Client) ListTeamMembers(id int64, opt ListTeamMembersOptions) ([]*User,
// GetTeamMember gets a member of a team // GetTeamMember gets a member of a team
func (c *Client) GetTeamMember(id int64, user string) (*User, *Response, error) { func (c *Client) GetTeamMember(id int64, user string) (*User, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
m := new(User) m := new(User)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil, m) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil, m)
return m, resp, err return m, resp, err
@ -166,12 +194,18 @@ func (c *Client) GetTeamMember(id int64, user string) (*User, *Response, error)
// AddTeamMember adds a member to a team // AddTeamMember adds a member to a team
func (c *Client) AddTeamMember(id int64, user string) (*Response, error) { func (c *Client) AddTeamMember(id int64, user string) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil)
return resp, err return resp, err
} }
// RemoveTeamMember removes a member from a team // RemoveTeamMember removes a member from a team
func (c *Client) RemoveTeamMember(id int64, user string) (*Response, error) { func (c *Client) RemoveTeamMember(id int64, user string) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil)
return resp, err return resp, err
} }
@ -191,12 +225,18 @@ func (c *Client) ListTeamRepositories(id int64, opt ListTeamRepositoriesOptions)
// AddTeamRepository adds a repository to a team // AddTeamRepository adds a repository to a team
func (c *Client) AddTeamRepository(id int64, org, repo string) (*Response, error) { func (c *Client) AddTeamRepository(id int64, org, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&org, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil)
return resp, err return resp, err
} }
// RemoveTeamRepository removes a repository from a team // RemoveTeamRepository removes a repository from a team
func (c *Client) RemoveTeamRepository(id int64, org, repo string) (*Response, error) { func (c *Client) RemoveTeamRepository(id int64, org, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&org, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil)
return resp, err return resp, err
} }

View File

@ -99,19 +99,37 @@ func (opt *ListPullRequestsOptions) QueryEncode() string {
// ListRepoPullRequests list PRs of one repository // ListRepoPullRequests list PRs of one repository
func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, *Response, error) { func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
prs := make([]*PullRequest, 0, opt.PageSize) prs := make([]*PullRequest, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", owner, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", owner, repo))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &prs) resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &prs)
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
for i := range prs {
if err := fixPullHeadSha(c, prs[i]); err != nil {
return prs, resp, err
}
}
}
return prs, resp, err return prs, resp, err
} }
// GetPullRequest get information of one PR // GetPullRequest get information of one PR
func (c *Client) GetPullRequest(owner, repo string, index int64) (*PullRequest, *Response, error) { func (c *Client) GetPullRequest(owner, repo string, index int64) (*PullRequest, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
pr := new(PullRequest) pr := new(PullRequest)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index), nil, nil, pr) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index), nil, nil, pr)
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
if err := fixPullHeadSha(c, pr); err != nil {
return pr, resp, err
}
}
return pr, resp, err return pr, resp, err
} }
@ -130,6 +148,9 @@ type CreatePullRequestOption struct {
// CreatePullRequest create pull request with options // CreatePullRequest create pull request with options
func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOption) (*PullRequest, *Response, error) { func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOption) (*PullRequest, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -169,6 +190,9 @@ func (opt EditPullRequestOption) Validate(c *Client) error {
// EditPullRequest modify pull request with PR id and options // EditPullRequest modify pull request with PR id and options
func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRequestOption) (*PullRequest, *Response, error) { func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRequestOption) (*PullRequest, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -202,6 +226,9 @@ func (opt MergePullRequestOption) Validate(c *Client) error {
// MergePullRequest merge a PR to repository by PR id // MergePullRequest merge a PR to repository by PR id
func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePullRequestOption) (bool, *Response, error) { func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePullRequestOption) (bool, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return false, nil, err
}
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return false, nil, err return false, nil, err
} }
@ -218,6 +245,9 @@ func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePull
// IsPullRequestMerged test if one PR is merged to one repository // IsPullRequestMerged test if one PR is merged to one repository
func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Response, error) { func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil) status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil)
if err != nil { if err != nil {
@ -229,6 +259,9 @@ func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Re
// getPullRequestDiffOrPatch gets the patch or diff file as bytes for a PR // getPullRequestDiffOrPatch gets the patch or diff file as bytes for a PR
func (c *Client) getPullRequestDiffOrPatch(owner, repo, kind string, index int64) ([]byte, *Response, error) { func (c *Client) getPullRequestDiffOrPatch(owner, repo, kind string, index int64) ([]byte, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &kind); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
r, _, err2 := c.GetRepo(owner, repo) r, _, err2 := c.GetRepo(owner, repo)
if err2 != nil { if err2 != nil {
@ -251,3 +284,41 @@ func (c *Client) GetPullRequestPatch(owner, repo string, index int64) ([]byte, *
func (c *Client) GetPullRequestDiff(owner, repo string, index int64) ([]byte, *Response, error) { func (c *Client) GetPullRequestDiff(owner, repo string, index int64) ([]byte, *Response, error) {
return c.getPullRequestDiffOrPatch(owner, repo, "diff", index) return c.getPullRequestDiffOrPatch(owner, repo, "diff", index)
} }
// ListPullRequestCommitsOptions options for listing pull requests
type ListPullRequestCommitsOptions struct {
ListOptions
}
// ListPullRequestCommits list commits for a pull request
func (c *Client) ListPullRequestCommits(owner, repo string, index int64, opt ListPullRequestCommitsOptions) ([]*Commit, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/commits", owner, repo, index))
opt.setDefaults()
commits := make([]*Commit, 0, opt.PageSize)
link.RawQuery = opt.getURLQuery().Encode()
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &commits)
return commits, resp, err
}
// fixPullHeadSha is a workaround for https://github.com/go-gitea/gitea/issues/12675
// When no head sha is available, this is because the branch got deleted in the base repo.
// pr.Head.Ref points in this case not to the head repo branch name, but the base repo ref,
// which stays available to resolve the commit sha. This is fixed for gitea >= 1.14.0
func fixPullHeadSha(client *Client, pr *PullRequest) error {
if pr.Base != nil && pr.Base.Repository != nil && pr.Base.Repository.Owner != nil &&
pr.Head != nil && pr.Head.Ref != "" && pr.Head.Sha == "" {
owner := pr.Base.Repository.Owner.UserName
repo := pr.Base.Repository.Name
refs, _, err := client.GetRepoRefs(owner, repo, pr.Head.Ref)
if err != nil {
return err
} else if len(refs) == 0 {
return fmt.Errorf("unable to resolve PR ref '%s'", pr.Head.Ref)
}
pr.Head.Sha = refs[0].Object.SHA
}
return nil
}

View File

@ -57,6 +57,7 @@ type PullReviewComment struct {
Body string `json:"body"` Body string `json:"body"`
Reviewer *User `json:"user"` Reviewer *User `json:"user"`
ReviewID int64 `json:"pull_request_review_id"` ReviewID int64 `json:"pull_request_review_id"`
Resolver *User `json:"resolver"`
Created time.Time `json:"created_at"` Created time.Time `json:"created_at"`
Updated time.Time `json:"updated_at"` Updated time.Time `json:"updated_at"`
@ -147,6 +148,9 @@ func (opt CreatePullReviewComment) Validate() error {
// ListPullReviews lists all reviews of a pull request // ListPullReviews lists all reviews of a pull request
func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullReviewsOptions) ([]*PullReview, *Response, error) { func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullReviewsOptions) ([]*PullReview, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -162,6 +166,9 @@ func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullRe
// GetPullReview gets a specific review of a pull request // GetPullReview gets a specific review of a pull request
func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview, *Response, error) { func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -173,6 +180,9 @@ func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview
// ListPullReviewComments lists all comments of a pull request review // ListPullReviewComments lists all comments of a pull request review
func (c *Client) ListPullReviewComments(owner, repo string, index, id int64) ([]*PullReviewComment, *Response, error) { func (c *Client) ListPullReviewComments(owner, repo string, index, id int64) ([]*PullReviewComment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -185,6 +195,9 @@ func (c *Client) ListPullReviewComments(owner, repo string, index, id int64) ([]
// DeletePullReview delete a specific review from a pull request // DeletePullReview delete a specific review from a pull request
func (c *Client) DeletePullReview(owner, repo string, index, id int64) (*Response, error) { func (c *Client) DeletePullReview(owner, repo string, index, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, err return nil, err
} }
@ -195,6 +208,9 @@ func (c *Client) DeletePullReview(owner, repo string, index, id int64) (*Respons
// CreatePullReview create a review to an pull request // CreatePullReview create a review to an pull request
func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePullReviewOptions) (*PullReview, *Response, error) { func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePullReviewOptions) (*PullReview, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -215,6 +231,9 @@ func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePul
// SubmitPullReview submit a pending review to an pull request // SubmitPullReview submit a pending review to an pull request
func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt SubmitPullReviewOptions) (*PullReview, *Response, error) { func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt SubmitPullReviewOptions) (*PullReview, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -235,6 +254,9 @@ func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt Submi
// CreateReviewRequests create review requests to an pull request // CreateReviewRequests create review requests to an pull request
func (c *Client) CreateReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) { func (c *Client) CreateReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err return nil, err
} }
@ -251,6 +273,9 @@ func (c *Client) CreateReviewRequests(owner, repo string, index int64, opt PullR
// DeleteReviewRequests delete review requests to an pull request // DeleteReviewRequests delete review requests to an pull request
func (c *Client) DeleteReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) { func (c *Client) DeleteReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err return nil, err
} }
@ -267,6 +292,9 @@ func (c *Client) DeleteReviewRequests(owner, repo string, index int64, opt PullR
// DismissPullReview dismiss a review for a pull request // DismissPullReview dismiss a review for a pull request
func (c *Client) DismissPullReview(owner, repo string, index, id int64, opt DismissPullReviewOptions) (*Response, error) { func (c *Client) DismissPullReview(owner, repo string, index, id int64, opt DismissPullReviewOptions) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err return nil, err
} }
@ -283,6 +311,9 @@ func (c *Client) DismissPullReview(owner, repo string, index, id int64, opt Dism
// UnDismissPullReview cancel to dismiss a review for a pull request // UnDismissPullReview cancel to dismiss a review for a pull request
func (c *Client) UnDismissPullReview(owner, repo string, index, id int64) (*Response, error) { func (c *Client) UnDismissPullReview(owner, repo string, index, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err return nil, err
} }

View File

@ -35,35 +35,60 @@ type Release struct {
// ListReleasesOptions options for listing repository's releases // ListReleasesOptions options for listing repository's releases
type ListReleasesOptions struct { type ListReleasesOptions struct {
ListOptions ListOptions
IsDraft *bool
IsPreRelease *bool
}
// QueryEncode turns options into querystring argument
func (opt *ListReleasesOptions) QueryEncode() string {
query := opt.getURLQuery()
if opt.IsDraft != nil {
query.Add("draft", fmt.Sprintf("%t", *opt.IsDraft))
}
if opt.IsPreRelease != nil {
query.Add("draft", fmt.Sprintf("%t", *opt.IsPreRelease))
}
return query.Encode()
} }
// ListReleases list releases of a repository // ListReleases list releases of a repository
func (c *Client) ListReleases(user, repo string, opt ListReleasesOptions) ([]*Release, *Response, error) { func (c *Client) ListReleases(owner, repo string, opt ListReleasesOptions) ([]*Release, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
releases := make([]*Release, 0, opt.PageSize) releases := make([]*Release, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases?%s", user, repo, opt.getURLQuery().Encode()), fmt.Sprintf("/repos/%s/%s/releases?%s", owner, repo, opt.QueryEncode()),
nil, nil, &releases) nil, nil, &releases)
return releases, resp, err return releases, resp, err
} }
// GetRelease get a release of a repository by id // GetRelease get a release of a repository by id
func (c *Client) GetRelease(user, repo string, id int64) (*Release, *Response, error) { func (c *Client) GetRelease(owner, repo string, id int64) (*Release, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
r := new(Release) r := new(Release)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), fmt.Sprintf("/repos/%s/%s/releases/%d", owner, repo, id),
jsonHeader, nil, &r) jsonHeader, nil, &r)
return r, resp, err return r, resp, err
} }
// GetReleaseByTag get a release of a repository by tag // GetReleaseByTag get a release of a repository by tag
func (c *Client) GetReleaseByTag(user, repo string, tag string) (*Release, *Response, error) { func (c *Client) GetReleaseByTag(owner, repo string, tag string) (*Release, *Response, error) {
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil { if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
return c.fallbackGetReleaseByTag(user, repo, tag) return c.fallbackGetReleaseByTag(owner, repo, tag)
}
if err := escapeValidatePathSegments(&owner, &repo, &tag); err != nil {
return nil, nil, err
} }
r := new(Release) r := new(Release)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases/tags/%s", user, repo, tag), fmt.Sprintf("/repos/%s/%s/releases/tags/%s", owner, repo, tag),
nil, nil, &r) nil, nil, &r)
return r, resp, err return r, resp, err
} }
@ -87,7 +112,10 @@ func (opt CreateReleaseOption) Validate() error {
} }
// CreateRelease create a release // CreateRelease create a release
func (c *Client) CreateRelease(user, repo string, opt CreateReleaseOption) (*Release, *Response, error) { func (c *Client) CreateRelease(owner, repo string, opt CreateReleaseOption) (*Release, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -97,7 +125,7 @@ func (c *Client) CreateRelease(user, repo string, opt CreateReleaseOption) (*Rel
} }
r := new(Release) r := new(Release)
resp, err := c.getParsedResponse("POST", resp, err := c.getParsedResponse("POST",
fmt.Sprintf("/repos/%s/%s/releases", user, repo), fmt.Sprintf("/repos/%s/%s/releases", owner, repo),
jsonHeader, bytes.NewReader(body), r) jsonHeader, bytes.NewReader(body), r)
return r, resp, err return r, resp, err
} }
@ -113,20 +141,26 @@ type EditReleaseOption struct {
} }
// EditRelease edit a release // EditRelease edit a release
func (c *Client) EditRelease(user, repo string, id int64, form EditReleaseOption) (*Release, *Response, error) { func (c *Client) EditRelease(owner, repo string, id int64, form EditReleaseOption) (*Release, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(form) body, err := json.Marshal(form)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
r := new(Release) r := new(Release)
resp, err := c.getParsedResponse("PATCH", resp, err := c.getParsedResponse("PATCH",
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), fmt.Sprintf("/repos/%s/%s/releases/%d", owner, repo, id),
jsonHeader, bytes.NewReader(body), r) jsonHeader, bytes.NewReader(body), r)
return r, resp, err return r, resp, err
} }
// DeleteRelease delete a release from a repository, keeping its tag // DeleteRelease delete a release from a repository, keeping its tag
func (c *Client) DeleteRelease(user, repo string, id int64) (*Response, error) { func (c *Client) DeleteRelease(user, repo string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", _, resp, err := c.getResponse("DELETE",
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id),
nil, nil) nil, nil)
@ -135,6 +169,9 @@ func (c *Client) DeleteRelease(user, repo string, id int64) (*Response, error) {
// DeleteReleaseByTag deletes a release frm a repository by tag // DeleteReleaseByTag deletes a release frm a repository by tag
func (c *Client) DeleteReleaseByTag(user, repo string, tag string) (*Response, error) { func (c *Client) DeleteReleaseByTag(user, repo string, tag string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err return nil, err
} }
@ -145,9 +182,9 @@ func (c *Client) DeleteReleaseByTag(user, repo string, tag string) (*Response, e
} }
// fallbackGetReleaseByTag is fallback for old gitea installations ( < 1.13.0 ) // fallbackGetReleaseByTag is fallback for old gitea installations ( < 1.13.0 )
func (c *Client) fallbackGetReleaseByTag(user, repo string, tag string) (*Release, *Response, error) { func (c *Client) fallbackGetReleaseByTag(owner, repo string, tag string) (*Release, *Response, error) {
for i := 1; ; i++ { for i := 1; ; i++ {
rl, resp, err := c.ListReleases(user, repo, ListReleasesOptions{ListOptions{Page: i}}) rl, resp, err := c.ListReleases(owner, repo, ListReleasesOptions{ListOptions: ListOptions{Page: i}})
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -93,6 +93,7 @@ type Repository struct {
AvatarURL string `json:"avatar_url"` AvatarURL string `json:"avatar_url"`
Internal bool `json:"internal"` Internal bool `json:"internal"`
MirrorInterval string `json:"mirror_interval"` MirrorInterval string `json:"mirror_interval"`
DefaultMergeStyle MergeStyle `json:"default_merge_style"`
} }
// RepoType represent repo type // RepoType represent repo type
@ -138,6 +139,9 @@ func (c *Client) ListMyRepos(opt ListReposOptions) ([]*Repository, *Response, er
// ListUserRepos list all repositories of one user by user's name // ListUserRepos list all repositories of one user by user's name
func (c *Client) ListUserRepos(user string, opt ListReposOptions) ([]*Repository, *Response, error) { func (c *Client) ListUserRepos(user string, opt ListReposOptions) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
repos := make([]*Repository, 0, opt.PageSize) repos := make([]*Repository, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos?%s", user, opt.getURLQuery().Encode()), nil, nil, &repos) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos?%s", user, opt.getURLQuery().Encode()), nil, nil, &repos)
@ -151,6 +155,9 @@ type ListOrgReposOptions struct {
// ListOrgRepos list all repositories of one organization by organization's name // ListOrgRepos list all repositories of one organization by organization's name
func (c *Client) ListOrgRepos(org string, opt ListOrgReposOptions) ([]*Repository, *Response, error) { func (c *Client) ListOrgRepos(org string, opt ListOrgReposOptions) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
repos := make([]*Repository, 0, opt.PageSize) repos := make([]*Repository, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos?%s", org, opt.getURLQuery().Encode()), nil, nil, &repos) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos?%s", org, opt.getURLQuery().Encode()), nil, nil, &repos)
@ -351,6 +358,9 @@ func (c *Client) CreateRepo(opt CreateRepoOption) (*Repository, *Response, error
// CreateOrgRepo creates an organization repository for authenticated user. // CreateOrgRepo creates an organization repository for authenticated user.
func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *Response, error) { func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -365,11 +375,21 @@ func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *
// GetRepo returns information of a repository of given owner. // GetRepo returns information of a repository of given owner.
func (c *Client) GetRepo(owner, reponame string) (*Repository, *Response, error) { func (c *Client) GetRepo(owner, reponame string) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
return nil, nil, err
}
repo := new(Repository) repo := new(Repository)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo)
return repo, resp, err return repo, resp, err
} }
// GetRepoByID returns information of a repository by a giver repository ID.
func (c *Client) GetRepoByID(id int64) (*Repository, *Response, error) {
repo := new(Repository)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repositories/%d", id), nil, nil, repo)
return repo, resp, err
}
// EditRepoOption options when editing a repository's properties // EditRepoOption options when editing a repository's properties
type EditRepoOption struct { type EditRepoOption struct {
// name of the repository // name of the repository
@ -414,10 +434,20 @@ type EditRepoOption struct {
Archived *bool `json:"archived,omitempty"` Archived *bool `json:"archived,omitempty"`
// set to a string like `8h30m0s` to set the mirror interval time // set to a string like `8h30m0s` to set the mirror interval time
MirrorInterval *string `json:"mirror_interval,omitempty"` MirrorInterval *string `json:"mirror_interval,omitempty"`
// either `true` to allow mark pr as merged manually, or `false` to prevent it. `has_pull_requests` must be `true`.
AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
// either `true` to enable AutodetectManualMerge, or `false` to prevent it. `has_pull_requests` must be `true`, Note: In some special cases, misjudgments can occur.
AutodetectManualMerge *bool `json:"autodetect_manual_merge,omitempty"`
// set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". `has_pull_requests` must be `true`.
DefaultMergeStyle *MergeStyle `json:"default_merge_style,omitempty"`
// set to `true` to archive this repository.
} }
// EditRepo edit the properties of a repository // EditRepo edit the properties of a repository
func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Repository, *Response, error) { func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -429,18 +459,27 @@ func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Reposito
// DeleteRepo deletes a repository of user or organization. // DeleteRepo deletes a repository of user or organization.
func (c *Client) DeleteRepo(owner, repo string) (*Response, error) { func (c *Client) DeleteRepo(owner, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil)
return resp, err return resp, err
} }
// MirrorSync adds a mirrored repository to the mirror sync queue. // MirrorSync adds a mirrored repository to the mirror sync queue.
func (c *Client) MirrorSync(owner, repo string) (*Response, error) { func (c *Client) MirrorSync(owner, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), nil, nil) _, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), nil, nil)
return resp, err return resp, err
} }
// GetRepoLanguages return language stats of a repo // GetRepoLanguages return language stats of a repo
func (c *Client) GetRepoLanguages(owner, repo string) (map[string]int64, *Response, error) { func (c *Client) GetRepoLanguages(owner, repo string) (map[string]int64, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
langMap := make(map[string]int64) langMap := make(map[string]int64)
data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/languages", owner, repo), jsonHeader, nil) data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/languages", owner, repo), jsonHeader, nil)
@ -466,7 +505,11 @@ const (
// GetArchive get an archive of a repository by git reference // GetArchive get an archive of a repository by git reference
// e.g.: ref -> master, 70b7c74b33, v1.2.1, ... // e.g.: ref -> master, 70b7c74b33, v1.2.1, ...
func (c *Client) GetArchive(owner, repo, ref string, ext ArchiveType) ([]byte, *Response, error) { func (c *Client) GetArchive(owner, repo, ref string, ext ArchiveType) ([]byte, *Response, error) {
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, url.PathEscape(ref), ext), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
ref = pathEscapeSegments(ref)
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
} }
// GetArchiveReader gets a `git archive` for a particular tree-ish git reference // GetArchiveReader gets a `git archive` for a particular tree-ish git reference
@ -474,7 +517,11 @@ func (c *Client) GetArchive(owner, repo, ref string, ext ArchiveType) ([]byte, *
// (`v1.2.1`). The archive is returned as a byte stream in a ReadCloser. It is // (`v1.2.1`). The archive is returned as a byte stream in a ReadCloser. It is
// the responsibility of the client to close the reader. // the responsibility of the client to close the reader.
func (c *Client) GetArchiveReader(owner, repo, ref string, ext ArchiveType) (io.ReadCloser, *Response, error) { func (c *Client) GetArchiveReader(owner, repo, ref string, ext ArchiveType) (io.ReadCloser, *Response, error) {
resp, err := c.doRequest("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, url.PathEscape(ref), ext), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
ref = pathEscapeSegments(ref)
resp, err := c.doRequest("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -20,9 +20,6 @@ type PayloadUser struct {
UserName string `json:"username"` UserName string `json:"username"`
} }
// FIXME: consider using same format as API when commits API are added.
// applies to PayloadCommit and PayloadCommitVerification
// PayloadCommit represents a commit // PayloadCommit represents a commit
type PayloadCommit struct { type PayloadCommit struct {
// sha1 hash of the commit // sha1 hash of the commit
@ -66,6 +63,9 @@ type ListRepoBranchesOptions struct {
// ListRepoBranches list all the branches of one repository // ListRepoBranches list all the branches of one repository
func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions) ([]*Branch, *Response, error) { func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions) ([]*Branch, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
branches := make([]*Branch, 0, opt.PageSize) branches := make([]*Branch, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &branches) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &branches)
@ -74,6 +74,9 @@ func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions
// GetRepoBranch get one branch's information of one repository // GetRepoBranch get one branch's information of one repository
func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, *Response, error) { func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &branch); err != nil {
return nil, nil, err
}
b := new(Branch) b := new(Branch)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b)
if err != nil { if err != nil {
@ -84,6 +87,9 @@ func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, *Response, e
// DeleteRepoBranch delete a branch in a repository // DeleteRepoBranch delete a branch in a repository
func (c *Client) DeleteRepoBranch(user, repo, branch string) (bool, *Response, error) { func (c *Client) DeleteRepoBranch(user, repo, branch string) (bool, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &branch); err != nil {
return false, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return false, nil, err return false, nil, err
} }
@ -118,6 +124,9 @@ func (opt CreateBranchOption) Validate() error {
// CreateBranch creates a branch for a user's repository // CreateBranch creates a branch for a user's repository
func (c *Client) CreateBranch(owner, repo string, opt CreateBranchOption) (*Branch, *Response, error) { func (c *Client) CreateBranch(owner, repo string, opt CreateBranchOption) (*Branch, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -95,6 +95,9 @@ type ListBranchProtectionsOptions struct {
// ListBranchProtections list branch protections for a repo // ListBranchProtections list branch protections for a repo
func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtectionsOptions) ([]*BranchProtection, *Response, error) { func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtectionsOptions) ([]*BranchProtection, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -107,6 +110,9 @@ func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtect
// GetBranchProtection gets a branch protection // GetBranchProtection gets a branch protection
func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtection, *Response, error) { func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtection, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -117,6 +123,9 @@ func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtectio
// CreateBranchProtection creates a branch protection for a repo // CreateBranchProtection creates a branch protection for a repo
func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProtectionOption) (*BranchProtection, *Response, error) { func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProtectionOption) (*BranchProtection, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -131,6 +140,9 @@ func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProt
// EditBranchProtection edits a branch protection for a repo // EditBranchProtection edits a branch protection for a repo
func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchProtectionOption) (*BranchProtection, *Response, error) { func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchProtectionOption) (*BranchProtection, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@ -145,6 +157,9 @@ func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchPr
// DeleteBranchProtection deletes a branch protection for a repo // DeleteBranchProtection deletes a branch protection for a repo
func (c *Client) DeleteBranchProtection(owner, repo, name string) (*Response, error) { func (c *Client) DeleteBranchProtection(owner, repo, name string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, err return nil, err
} }

View File

@ -1,3 +1,4 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -17,6 +18,9 @@ type ListCollaboratorsOptions struct {
// ListCollaborators list a repository's collaborators // ListCollaborators list a repository's collaborators
func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptions) ([]*User, *Response, error) { func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptions) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
collaborators := make([]*User, 0, opt.PageSize) collaborators := make([]*User, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
@ -27,6 +31,9 @@ func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptio
// IsCollaborator check if a user is a collaborator of a repository // IsCollaborator check if a user is a collaborator of a repository
func (c *Client) IsCollaborator(user, repo, collaborator string) (bool, *Response, error) { func (c *Client) IsCollaborator(user, repo, collaborator string) (bool, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil) status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
@ -78,6 +85,9 @@ func (opt AddCollaboratorOption) Validate() error {
// AddCollaborator add some user as a collaborator of a repository // AddCollaborator add some user as a collaborator of a repository
func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollaboratorOption) (*Response, error) { func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollaboratorOption) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
return nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, err return nil, err
} }
@ -91,7 +101,36 @@ func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollabo
// DeleteCollaborator remove a collaborator from a repository // DeleteCollaborator remove a collaborator from a repository
func (c *Client) DeleteCollaborator(user, repo, collaborator string) (*Response, error) { func (c *Client) DeleteCollaborator(user, repo, collaborator string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", _, resp, err := c.getResponse("DELETE",
fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil) fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
return resp, err return resp, err
} }
// GetReviewers return all users that can be requested to review in this repo
func (c *Client) GetReviewers(user, repo string) ([]*User, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
reviewers := make([]*User, 0, 5)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/reviewers", user, repo), nil, nil, &reviewers)
return reviewers, resp, err
}
// GetAssignees return all users that have write access and can be assigned to issues
func (c *Client) GetAssignees(user, repo string) ([]*User, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
assignees := make([]*User, 0, 5)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/assignees", user, repo), nil, nil, &assignees)
return assignees, resp, err
}

View File

@ -63,6 +63,9 @@ type CommitAffectedFiles struct {
// GetSingleCommit returns a single commit // GetSingleCommit returns a single commit
func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, *Response, error) { func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &commitID); err != nil {
return nil, nil, err
}
commit := new(Commit) commit := new(Commit)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit)
return commit, resp, err return commit, resp, err
@ -86,6 +89,9 @@ func (opt *ListCommitOptions) QueryEncode() string {
// ListRepoCommits return list of commits from a repo // ListRepoCommits return list of commits from a repo
func (c *Client) ListRepoCommits(user, repo string, opt ListCommitOptions) ([]*Commit, *Response, error) { func (c *Client) ListRepoCommits(user, repo string, opt ListCommitOptions) ([]*Commit, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/commits", user, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/commits", user, repo))
opt.setDefaults() opt.setDefaults()
commits := make([]*Commit, 0, opt.PageSize) commits := make([]*Commit, 0, opt.PageSize)

View File

@ -116,19 +116,12 @@ type FileDeleteResponse struct {
Verification *PayloadCommitVerification `json:"verification"` Verification *PayloadCommitVerification `json:"verification"`
} }
// pathEscapeSegments escapes segments of a path while not escaping forward slash
func pathEscapeSegments(path string) string {
slice := strings.Split(path, "/")
for index := range slice {
slice[index] = url.PathEscape(slice[index])
}
escapedPath := strings.Join(slice, "/")
return escapedPath
}
// GetFile downloads a file of repository, ref can be branch/tag/commit. // GetFile downloads a file of repository, ref can be branch/tag/commit.
// e.g.: ref -> master, filepath -> README.md (no leading slash) // e.g.: ref -> master, filepath -> README.md (no leading slash)
func (c *Client) GetFile(owner, repo, ref, filepath string) ([]byte, *Response, error) { func (c *Client) GetFile(owner, repo, ref, filepath string) ([]byte, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
filepath = pathEscapeSegments(filepath) filepath = pathEscapeSegments(filepath)
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil { if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
ref = pathEscapeSegments(ref) ref = pathEscapeSegments(ref)
@ -166,17 +159,23 @@ func (c *Client) ListContents(owner, repo, ref, filepath string) ([]*ContentsRes
} }
func (c *Client) getDirOrFileContents(owner, repo, ref, filepath string) ([]byte, *Response, error) { func (c *Client) getDirOrFileContents(owner, repo, ref, filepath string) ([]byte, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
filepath = pathEscapeSegments(strings.TrimPrefix(filepath, "/")) filepath = pathEscapeSegments(strings.TrimPrefix(filepath, "/"))
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), jsonHeader, nil) return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), jsonHeader, nil)
} }
// CreateFile create a file in a repository // CreateFile create a file in a repository
func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions) (*FileResponse, *Response, error) { func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions) (*FileResponse, *Response, error) {
filepath = pathEscapeSegments(filepath)
var err error var err error
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil { if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
filepath = pathEscapeSegments(filepath)
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
@ -189,12 +188,16 @@ func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions)
// UpdateFile update a file in a repository // UpdateFile update a file in a repository
func (c *Client) UpdateFile(owner, repo, filepath string, opt UpdateFileOptions) (*FileResponse, *Response, error) { func (c *Client) UpdateFile(owner, repo, filepath string, opt UpdateFileOptions) (*FileResponse, *Response, error) {
filepath = pathEscapeSegments(filepath)
var err error var err error
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil { if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
filepath = pathEscapeSegments(filepath)
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -206,11 +209,14 @@ func (c *Client) UpdateFile(owner, repo, filepath string, opt UpdateFileOptions)
// DeleteFile delete a file from repository // DeleteFile delete a file from repository
func (c *Client) DeleteFile(owner, repo, filepath string, opt DeleteFileOptions) (*Response, error) { func (c *Client) DeleteFile(owner, repo, filepath string, opt DeleteFileOptions) (*Response, error) {
filepath = pathEscapeSegments(filepath)
var err error var err error
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil { if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
return nil, err return nil, err
} }
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
filepath = pathEscapeSegments(filepath)
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {

View File

@ -46,6 +46,9 @@ func (opt *ListDeployKeysOptions) QueryEncode() string {
// ListDeployKeys list all the deploy keys of one repository // ListDeployKeys list all the deploy keys of one repository
func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([]*DeployKey, *Response, error) { func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([]*DeployKey, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/keys", user, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/keys", user, repo))
opt.setDefaults() opt.setDefaults()
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
@ -56,6 +59,9 @@ func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([
// GetDeployKey get one deploy key with key id // GetDeployKey get one deploy key with key id
func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, *Response, error) { func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
key := new(DeployKey) key := new(DeployKey)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys/%d", user, repo, keyID), nil, nil, &key) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys/%d", user, repo, keyID), nil, nil, &key)
return key, resp, err return key, resp, err
@ -63,6 +69,9 @@ func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, *Resp
// CreateDeployKey options when create one deploy key // CreateDeployKey options when create one deploy key
func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*DeployKey, *Response, error) { func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*DeployKey, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -74,6 +83,9 @@ func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*Deplo
// DeleteDeployKey delete deploy key with key id // DeleteDeployKey delete deploy key with key id
func (c *Client) DeleteDeployKey(owner, repo string, keyID int64) (*Response, error) { func (c *Client) DeleteDeployKey(owner, repo string, keyID int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/keys/%d", owner, repo, keyID), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/keys/%d", owner, repo, keyID), nil, nil)
return resp, err return resp, err
} }

View File

@ -47,6 +47,8 @@ type MigrateRepoOption struct {
PullRequests bool `json:"pull_requests"` PullRequests bool `json:"pull_requests"`
Releases bool `json:"releases"` Releases bool `json:"releases"`
MirrorInterval string `json:"mirror_interval"` MirrorInterval string `json:"mirror_interval"`
LFS bool `json:"lfs"`
LFSEndpoint string `json:"lfs_endpoint"`
} }
// Validate the MigrateRepoOption struct // Validate the MigrateRepoOption struct

View File

@ -27,7 +27,11 @@ type GitObject struct {
// GetRepoRef get one ref's information of one repository // GetRepoRef get one ref's information of one repository
func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, *Response, error) { func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
ref = strings.TrimPrefix(ref, "refs/") ref = strings.TrimPrefix(ref, "refs/")
ref = pathEscapeSegments(ref)
r := new(Reference) r := new(Reference)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil, &r) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil, &r)
if _, ok := err.(*json.UnmarshalTypeError); ok { if _, ok := err.(*json.UnmarshalTypeError); ok {
@ -42,7 +46,12 @@ func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, *Response, erro
// GetRepoRefs get list of ref's information of one repository // GetRepoRefs get list of ref's information of one repository
func (c *Client) GetRepoRefs(user, repo, ref string) ([]*Reference, *Response, error) { func (c *Client) GetRepoRefs(user, repo, ref string) ([]*Reference, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
ref = strings.TrimPrefix(ref, "refs/") ref = strings.TrimPrefix(ref, "refs/")
ref = pathEscapeSegments(ref)
data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil) data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err

View File

@ -16,6 +16,9 @@ type ListStargazersOptions struct {
// ListRepoStargazers list a repository's stargazers // ListRepoStargazers list a repository's stargazers
func (c *Client) ListRepoStargazers(user, repo string, opt ListStargazersOptions) ([]*User, *Response, error) { func (c *Client) ListRepoStargazers(user, repo string, opt ListStargazersOptions) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
stargazers := make([]*User, 0, opt.PageSize) stargazers := make([]*User, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/stargazers?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &stargazers) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/stargazers?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &stargazers)
@ -24,6 +27,9 @@ func (c *Client) ListRepoStargazers(user, repo string, opt ListStargazersOptions
// GetStarredRepos returns the repos that the given user has starred // GetStarredRepos returns the repos that the given user has starred
func (c *Client) GetStarredRepos(user string) ([]*Repository, *Response, error) { func (c *Client) GetStarredRepos(user string) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
repos := make([]*Repository, 0, 10) repos := make([]*Repository, 0, 10)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/starred", user), jsonHeader, nil, &repos) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/starred", user), jsonHeader, nil, &repos)
return repos, resp, err return repos, resp, err
@ -38,6 +44,9 @@ func (c *Client) GetMyStarredRepos() ([]*Repository, *Response, error) {
// IsRepoStarring returns whether the authenticated user has starred the repo or not // IsRepoStarring returns whether the authenticated user has starred the repo or not
func (c *Client) IsRepoStarring(user, repo string) (bool, *Response, error) { func (c *Client) IsRepoStarring(user, repo string) (bool, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return false, nil, err
}
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil) _, resp, err := c.getResponse("GET", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
if resp != nil { if resp != nil {
switch resp.StatusCode { switch resp.StatusCode {
@ -54,6 +63,9 @@ func (c *Client) IsRepoStarring(user, repo string) (bool, *Response, error) {
// StarRepo star specified repo as the authenticated user // StarRepo star specified repo as the authenticated user
func (c *Client) StarRepo(user, repo string) (*Response, error) { func (c *Client) StarRepo(user, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
if resp != nil { if resp != nil {
switch resp.StatusCode { switch resp.StatusCode {
@ -68,6 +80,9 @@ func (c *Client) StarRepo(user, repo string) (*Response, error) {
// UnStarRepo remove star to specified repo as the authenticated user // UnStarRepo remove star to specified repo as the authenticated user
func (c *Client) UnStarRepo(user, repo string) (*Response, error) { func (c *Client) UnStarRepo(user, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
if resp != nil { if resp != nil {
switch resp.StatusCode { switch resp.StatusCode {

View File

@ -5,18 +5,39 @@
package gitea package gitea
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
) )
// Tag represents a repository tag // Tag represents a repository tag
type Tag struct { type Tag struct {
Name string `json:"name"` Name string `json:"name"`
Message string `json:"message"`
ID string `json:"id"` ID string `json:"id"`
Commit *CommitMeta `json:"commit"` Commit *CommitMeta `json:"commit"`
ZipballURL string `json:"zipball_url"` ZipballURL string `json:"zipball_url"`
TarballURL string `json:"tarball_url"` TarballURL string `json:"tarball_url"`
} }
// AnnotatedTag represents an annotated tag
type AnnotatedTag struct {
Tag string `json:"tag"`
SHA string `json:"sha"`
URL string `json:"url"`
Message string `json:"message"`
Tagger *CommitUser `json:"tagger"`
Object *AnnotatedTagObject `json:"object"`
Verification *PayloadCommitVerification `json:"verification"`
}
// AnnotatedTagObject contains meta information of the tag object
type AnnotatedTagObject struct {
Type string `json:"type"`
URL string `json:"url"`
SHA string `json:"sha"`
}
// ListRepoTagsOptions options for listing a repository's tags // ListRepoTagsOptions options for listing a repository's tags
type ListRepoTagsOptions struct { type ListRepoTagsOptions struct {
ListOptions ListOptions
@ -24,14 +45,81 @@ type ListRepoTagsOptions struct {
// ListRepoTags list all the branches of one repository // ListRepoTags list all the branches of one repository
func (c *Client) ListRepoTags(user, repo string, opt ListRepoTagsOptions) ([]*Tag, *Response, error) { func (c *Client) ListRepoTags(user, repo string, opt ListRepoTagsOptions) ([]*Tag, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
tags := make([]*Tag, 0, opt.PageSize) tags := make([]*Tag, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &tags) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &tags)
return tags, resp, err return tags, resp, err
} }
// GetTag get the tag of a repository
func (c *Client) GetTag(user, repo, tag string) (*Tag, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
return nil, nil, err
}
t := new(Tag)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags/%s", user, repo, tag), nil, nil, &t)
return t, resp, err
}
// GetAnnotatedTag get the tag object of an annotated tag (not lightweight tags) of a repository
func (c *Client) GetAnnotatedTag(user, repo, sha string) (*AnnotatedTag, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
if err := escapeValidatePathSegments(&user, &repo, &sha); err != nil {
return nil, nil, err
}
t := new(AnnotatedTag)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/tags/%s", user, repo, sha), nil, nil, &t)
return t, resp, err
}
// CreateTagOption options when creating a tag
type CreateTagOption struct {
TagName string `json:"tag_name"`
Message string `json:"message"`
Target string `json:"target"`
}
// Validate validates CreateTagOption
func (opt CreateTagOption) Validate() error {
if len(opt.TagName) == 0 {
return fmt.Errorf("TagName is required")
}
return nil
}
// CreateTag create a new git tag in a repository
func (c *Client) CreateTag(user, repo string, opt CreateTagOption) (*Tag, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil {
return nil, nil, err
}
body, err := json.Marshal(opt)
if err != nil {
return nil, nil, err
}
t := new(Tag)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/tags", user, repo), jsonHeader, bytes.NewReader(body), &t)
return t, resp, err
}
// DeleteTag deletes a tag from a repository, if no release refers to it // DeleteTag deletes a tag from a repository, if no release refers to it
func (c *Client) DeleteTag(user, repo string, tag string) (*Response, error) { func (c *Client) DeleteTag(user, repo, tag string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err return nil, err
} }

65
vendor/code.gitea.io/sdk/gitea/repo_team.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
// 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 gitea
import (
"fmt"
"net/http"
)
// GetRepoTeams return teams from a repository
func (c *Client) GetRepoTeams(user, repo string) ([]*Team, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
teams := make([]*Team, 0, 5)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/teams", user, repo), nil, nil, &teams)
return teams, resp, err
}
// AddRepoTeam add a team to a repository
func (c *Client) AddRepoTeam(user, repo, team string) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, err
}
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil)
return resp, err
}
// RemoveRepoTeam delete a team from a repository
func (c *Client) RemoveRepoTeam(user, repo, team string) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, err
}
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil)
return resp, err
}
// CheckRepoTeam check if team is assigned to repo by name and return it.
// If not assigned, it will return nil.
func (c *Client) CheckRepoTeam(user, repo, team string) (*Team, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
if err := escapeValidatePathSegments(&user, &repo, &team); err != nil {
return nil, nil, err
}
t := new(Team)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/teams/%s", user, repo, team), nil, nil, &t)
if resp != nil && resp.StatusCode == http.StatusNotFound {
// if not found it's not an error, it indicates it's not assigned
return nil, resp, nil
}
return t, resp, err
}

65
vendor/code.gitea.io/sdk/gitea/repo_template.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
// 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 gitea
import (
"bytes"
"encoding/json"
"fmt"
)
// CreateRepoFromTemplateOption options when creating repository using a template
type CreateRepoFromTemplateOption struct {
// Owner is the organization or person who will own the new repository
Owner string `json:"owner"`
// Name of the repository to create
Name string `json:"name"`
// Description of the repository to create
Description string `json:"description"`
// Private is whether the repository is private
Private bool `json:"private"`
// GitContent include git content of default branch in template repo
GitContent bool `json:"git_content"`
// Topics include topics of template repo
Topics bool `json:"topics"`
// GitHooks include git hooks of template repo
GitHooks bool `json:"git_hooks"`
// Webhooks include webhooks of template repo
Webhooks bool `json:"webhooks"`
// Avatar include avatar of the template repo
Avatar bool `json:"avatar"`
// Labels include labels of template repo
Labels bool `json:"labels"`
}
// Validate validates CreateRepoFromTemplateOption
func (opt CreateRepoFromTemplateOption) Validate() error {
if len(opt.Owner) == 0 {
return fmt.Errorf("field Owner is required")
}
if len(opt.Name) == 0 {
return fmt.Errorf("field Name is required")
}
return nil
}
// CreateRepoFromTemplate create a repository using a template
func (c *Client) CreateRepoFromTemplate(templateOwner, templateRepo string, opt CreateRepoFromTemplateOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&templateOwner, &templateRepo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, nil, err
}
repo := new(Repository)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/generate", templateOwner, templateRepo), jsonHeader, bytes.NewReader(body), &repo)
return repo, resp, err
}

View File

@ -22,6 +22,9 @@ type topicsList struct {
// ListRepoTopics list all repository's topics // ListRepoTopics list all repository's topics
func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([]string, *Response, error) { func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([]string, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
list := new(topicsList) list := new(topicsList)
@ -34,9 +37,10 @@ func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([
// SetRepoTopics replaces the list of repo's topics // SetRepoTopics replaces the list of repo's topics
func (c *Client) SetRepoTopics(user, repo string, list []string) (*Response, error) { func (c *Client) SetRepoTopics(user, repo string, list []string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
l := topicsList{Topics: list} l := topicsList{Topics: list}
body, err := json.Marshal(&l) body, err := json.Marshal(&l)
if err != nil { if err != nil {
return nil, err return nil, err
@ -47,12 +51,18 @@ func (c *Client) SetRepoTopics(user, repo string, list []string) (*Response, err
// AddRepoTopic adds a topic to a repo's topics list // AddRepoTopic adds a topic to a repo's topics list
func (c *Client) AddRepoTopic(user, repo, topic string) (*Response, error) { func (c *Client) AddRepoTopic(user, repo, topic string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &topic); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
return resp, err return resp, err
} }
// DeleteRepoTopic deletes a topic from repo's topics list // DeleteRepoTopic deletes a topic from repo's topics list
func (c *Client) DeleteRepoTopic(user, repo, topic string) (*Response, error) { func (c *Client) DeleteRepoTopic(user, repo, topic string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &topic); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
return resp, err return resp, err
} }

View File

@ -20,6 +20,9 @@ type TransferRepoOption struct {
// TransferRepo transfers the ownership of a repository // TransferRepo transfers the ownership of a repository
func (c *Client) TransferRepo(owner, reponame string, opt TransferRepoOption) (*Repository, *Response, error) { func (c *Client) TransferRepo(owner, reponame string, opt TransferRepoOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -31,6 +31,9 @@ type GitTreeResponse struct {
// GetTrees downloads a file of repository, ref can be branch/tag/commit. // GetTrees downloads a file of repository, ref can be branch/tag/commit.
// e.g.: ref -> master, tree -> macaron.go(no leading slash) // e.g.: ref -> master, tree -> macaron.go(no leading slash)
func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, *Response, error) { func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &ref); err != nil {
return nil, nil, err
}
trees := new(GitTreeResponse) trees := new(GitTreeResponse)
var path = fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref) var path = fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref)
if recursive { if recursive {

View File

@ -22,6 +22,9 @@ type WatchInfo struct {
// GetWatchedRepos list all the watched repos of user // GetWatchedRepos list all the watched repos of user
func (c *Client) GetWatchedRepos(user string) ([]*Repository, *Response, error) { func (c *Client) GetWatchedRepos(user string) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
repos := make([]*Repository, 0, 10) repos := make([]*Repository, 0, 10)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), nil, nil, &repos) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), nil, nil, &repos)
return repos, resp, err return repos, resp, err
@ -35,8 +38,11 @@ func (c *Client) GetMyWatchedRepos() ([]*Repository, *Response, error) {
} }
// CheckRepoWatch check if the current user is watching a repo // CheckRepoWatch check if the current user is watching a repo
func (c *Client) CheckRepoWatch(repoUser, repoName string) (bool, *Response, error) { func (c *Client) CheckRepoWatch(owner, repo string) (bool, *Response, error) {
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
} }
@ -51,8 +57,11 @@ func (c *Client) CheckRepoWatch(repoUser, repoName string) (bool, *Response, err
} }
// WatchRepo start to watch a repository // WatchRepo start to watch a repository
func (c *Client) WatchRepo(repoUser, repoName string) (*Response, error) { func (c *Client) WatchRepo(owner, repo string) (*Response, error) {
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
if err != nil { if err != nil {
return resp, err return resp, err
} }
@ -63,8 +72,11 @@ func (c *Client) WatchRepo(repoUser, repoName string) (*Response, error) {
} }
// UnWatchRepo stop to watch a repository // UnWatchRepo stop to watch a repository
func (c *Client) UnWatchRepo(repoUser, repoName string) (*Response, error) { func (c *Client) UnWatchRepo(owner, repo string) (*Response, error) {
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
if err != nil { if err != nil {
return resp, err return resp, err
} }

View File

@ -8,13 +8,17 @@ package gitea
type GlobalUISettings struct { type GlobalUISettings struct {
DefaultTheme string `json:"default_theme"` DefaultTheme string `json:"default_theme"`
AllowedReactions []string `json:"allowed_reactions"` AllowedReactions []string `json:"allowed_reactions"`
CustomEmojis []string `json:"custom_emojis"`
} }
// GlobalRepoSettings represent the global repository settings of a gitea instance witch is exposed by API // GlobalRepoSettings represent the global repository settings of a gitea instance witch is exposed by API
type GlobalRepoSettings struct { type GlobalRepoSettings struct {
MirrorsDisabled bool `json:"mirrors_disabled"` MirrorsDisabled bool `json:"mirrors_disabled"`
HTTPGitDisabled bool `json:"http_git_disabled"` HTTPGitDisabled bool `json:"http_git_disabled"`
MigrationsDisabled bool `json:"migrations_disabled"` MigrationsDisabled bool `json:"migrations_disabled"`
StarsDisabled bool `json:"stars_disabled"`
TimeTrackingDisabled bool `json:"time_tracking_disabled"`
LFSDisabled bool `json:"lfs_disabled"`
} }
// GlobalAPISettings contains global api settings exposed by it // GlobalAPISettings contains global api settings exposed by it

View File

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"time" "time"
) )
@ -51,12 +52,15 @@ type CreateStatusOption struct {
// CreateStatus creates a new Status for a given Commit // CreateStatus creates a new Status for a given Commit
func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, *Response, error) { func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opts) body, err := json.Marshal(&opts)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
status := new(Status) status := new(Status)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, sha), jsonHeader, bytes.NewReader(body), status) resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, url.QueryEscape(sha)), jsonHeader, bytes.NewReader(body), status)
return status, resp, err return status, resp, err
} }
@ -67,6 +71,9 @@ type ListStatusesOption struct {
// ListStatuses returns all statuses for a given Commit by ref // ListStatuses returns all statuses for a given Commit by ref
func (c *Client) ListStatuses(owner, repo, ref string, opt ListStatusesOption) ([]*Status, *Response, error) { func (c *Client) ListStatuses(owner, repo, ref string, opt ListStatusesOption) ([]*Status, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &ref); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
statuses := make([]*Status, 0, opt.PageSize) statuses := make([]*Status, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?%s", owner, repo, ref, opt.getURLQuery().Encode()), jsonHeader, nil, &statuses) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?%s", owner, repo, ref, opt.getURLQuery().Encode()), jsonHeader, nil, &statuses)
@ -86,6 +93,9 @@ type CombinedStatus struct {
// GetCombinedStatus returns the CombinedStatus for a given Commit // GetCombinedStatus returns the CombinedStatus for a given Commit
func (c *Client) GetCombinedStatus(owner, repo, ref string) (*CombinedStatus, *Response, error) { func (c *Client) GetCombinedStatus(owner, repo, ref string) (*CombinedStatus, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &ref); err != nil {
return nil, nil, err
}
status := new(CombinedStatus) status := new(CombinedStatus)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, ref), jsonHeader, nil, status) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, ref), jsonHeader, nil, status)

View File

@ -6,6 +6,8 @@ package gitea
import ( import (
"fmt" "fmt"
"net/url"
"strconv"
"time" "time"
) )
@ -23,13 +25,37 @@ type User struct {
// User locale // User locale
Language string `json:"language"` Language string `json:"language"`
// Is the user an administrator // Is the user an administrator
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin"`
LastLogin time.Time `json:"last_login,omitempty"` // Date and Time of last login
Created time.Time `json:"created,omitempty"` LastLogin time.Time `json:"last_login"`
// Date and Time of user creation
Created time.Time `json:"created"`
// Is user restricted
Restricted bool `json:"restricted"`
// Is user active
IsActive bool `json:"active"`
// Is user login prohibited
ProhibitLogin bool `json:"prohibit_login"`
// the user's location
Location string `json:"location"`
// the user's website
Website string `json:"website"`
// the user's description
Description string `json:"description"`
// User visibility level option
Visibility VisibleType `json:"visibility"`
// user counts
FollowerCount int `json:"followers_count"`
FollowingCount int `json:"following_count"`
StarredRepoCount int `json:"starred_repos_count"`
} }
// GetUserInfo get user info by user's name // GetUserInfo get user info by user's name
func (c *Client) GetUserInfo(user string) (*User, *Response, error) { func (c *Client) GetUserInfo(user string) (*User, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
u := new(User) u := new(User)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s", user), nil, nil, u) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s", user), nil, nil, u)
return u, resp, err return u, resp, err
@ -41,3 +67,24 @@ func (c *Client) GetMyUserInfo() (*User, *Response, error) {
resp, err := c.getParsedResponse("GET", "/user", nil, nil, u) resp, err := c.getParsedResponse("GET", "/user", nil, nil, u)
return u, resp, err return u, resp, err
} }
// GetUserByID returns user by a given user ID
func (c *Client) GetUserByID(id int64) (*User, *Response, error) {
if id < 0 {
return nil, nil, fmt.Errorf("invalid user id %d", id)
}
query := make(url.Values)
query.Add("uid", strconv.FormatInt(id, 10))
users, resp, err := c.searchUsers(query.Encode())
if err != nil {
return nil, resp, err
}
if len(users) == 1 {
return users[0], resp, err
}
return nil, resp, fmt.Errorf("user not found with id %d", id)
}

View File

@ -9,6 +9,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"reflect" "reflect"
) )
@ -35,7 +36,7 @@ func (c *Client) ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken,
} }
opts.setDefaults() opts.setDefaults()
tokens := make([]*AccessToken, 0, opts.PageSize) tokens := make([]*AccessToken, 0, opts.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", username, opts.getURLQuery().Encode()), jsonHeader, nil, &tokens) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", url.PathEscape(username), opts.getURLQuery().Encode()), jsonHeader, nil, &tokens)
return tokens, resp, err return tokens, resp, err
} }
@ -57,7 +58,7 @@ func (c *Client) CreateAccessToken(opt CreateAccessTokenOption) (*AccessToken, *
return nil, nil, err return nil, nil, err
} }
t := new(AccessToken) t := new(AccessToken)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", username), jsonHeader, bytes.NewReader(body), t) resp, err := c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", url.PathEscape(username)), jsonHeader, bytes.NewReader(body), t)
return t, resp, err return t, resp, err
} }
@ -84,6 +85,6 @@ func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) {
return nil, fmt.Errorf("only string and int64 supported") return nil, fmt.Errorf("only string and int64 supported")
} }
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%s", username, token), jsonHeader, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%s", url.PathEscape(username), url.PathEscape(token)), jsonHeader, nil)
return resp, err return resp, err
} }

View File

@ -21,6 +21,9 @@ func (c *Client) ListMyFollowers(opt ListFollowersOptions) ([]*User, *Response,
// ListFollowers list all the followers of one user // ListFollowers list all the followers of one user
func (c *Client) ListFollowers(user string, opt ListFollowersOptions) ([]*User, *Response, error) { func (c *Client) ListFollowers(user string, opt ListFollowersOptions) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
users := make([]*User, 0, opt.PageSize) users := make([]*User, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?%s", user, opt.getURLQuery().Encode()), nil, nil, &users) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
@ -42,6 +45,9 @@ func (c *Client) ListMyFollowing(opt ListFollowingOptions) ([]*User, *Response,
// ListFollowing list all the users the user followed // ListFollowing list all the users the user followed
func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User, *Response, error) { func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
users := make([]*User, 0, opt.PageSize) users := make([]*User, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?%s", user, opt.getURLQuery().Encode()), nil, nil, &users) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
@ -50,24 +56,38 @@ func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User,
// IsFollowing if current user followed the target // IsFollowing if current user followed the target
func (c *Client) IsFollowing(target string) (bool, *Response) { func (c *Client) IsFollowing(target string) (bool, *Response) {
if err := escapeValidatePathSegments(&target); err != nil {
// ToDo return err
return false, nil
}
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/following/%s", target), nil, nil) _, resp, err := c.getResponse("GET", fmt.Sprintf("/user/following/%s", target), nil, nil)
return err == nil, resp return err == nil, resp
} }
// IsUserFollowing if the user followed the target // IsUserFollowing if the user followed the target
func (c *Client) IsUserFollowing(user, target string) (bool, *Response) { func (c *Client) IsUserFollowing(user, target string) (bool, *Response) {
if err := escapeValidatePathSegments(&user, &target); err != nil {
// ToDo return err
return false, nil
}
_, resp, err := c.getResponse("GET", fmt.Sprintf("/users/%s/following/%s", user, target), nil, nil) _, resp, err := c.getResponse("GET", fmt.Sprintf("/users/%s/following/%s", user, target), nil, nil)
return err == nil, resp return err == nil, resp
} }
// Follow set current user follow the target // Follow set current user follow the target
func (c *Client) Follow(target string) (*Response, error) { func (c *Client) Follow(target string) (*Response, error) {
if err := escapeValidatePathSegments(&target); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/following/%s", target), nil, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/following/%s", target), nil, nil)
return resp, err return resp, err
} }
// Unfollow set current user unfollow the target // Unfollow set current user unfollow the target
func (c *Client) Unfollow(target string) (*Response, error) { func (c *Client) Unfollow(target string) (*Response, error) {
if err := escapeValidatePathSegments(&target); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/following/%s", target), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/following/%s", target), nil, nil)
return resp, err return resp, err
} }

View File

@ -40,6 +40,9 @@ type ListGPGKeysOptions struct {
// ListGPGKeys list all the GPG keys of the user // ListGPGKeys list all the GPG keys of the user
func (c *Client) ListGPGKeys(user string, opt ListGPGKeysOptions) ([]*GPGKey, *Response, error) { func (c *Client) ListGPGKeys(user string, opt ListGPGKeysOptions) ([]*GPGKey, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
keys := make([]*GPGKey, 0, opt.PageSize) keys := make([]*GPGKey, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)

View File

@ -31,6 +31,9 @@ type ListPublicKeysOptions struct {
// ListPublicKeys list all the public keys of the user // ListPublicKeys list all the public keys of the user
func (c *Client) ListPublicKeys(user string, opt ListPublicKeysOptions) ([]*PublicKey, *Response, error) { func (c *Client) ListPublicKeys(user string, opt ListPublicKeysOptions) ([]*PublicKey, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
keys := make([]*PublicKey, 0, opt.PageSize) keys := make([]*PublicKey, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)

View File

@ -34,11 +34,15 @@ func (opt *SearchUsersOption) QueryEncode() string {
return query.Encode() return query.Encode()
} }
// SearchUsers finds users by query func (c *Client) searchUsers(rawQuery string) ([]*User, *Response, error) {
func (c *Client) SearchUsers(opt SearchUsersOption) ([]*User, *Response, error) {
link, _ := url.Parse("/users/search") link, _ := url.Parse("/users/search")
link.RawQuery = opt.QueryEncode() link.RawQuery = rawQuery
userResp := new(searchUsersResponse) userResp := new(searchUsersResponse)
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &userResp) resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &userResp)
return userResp.Users, resp, err return userResp.Users, resp, err
} }
// SearchUsers finds users by query
func (c *Client) SearchUsers(opt SearchUsersOption) ([]*User, *Response, error) {
return c.searchUsers(opt.QueryEncode())
}

62
vendor/code.gitea.io/sdk/gitea/user_settings.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
// 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 gitea
import (
"bytes"
"encoding/json"
)
// UserSettings represents user settings
type UserSettings struct {
FullName string `json:"full_name"`
Website string `json:"website"`
Description string `json:"description"`
Location string `json:"location"`
Language string `json:"language"`
Theme string `json:"theme"`
DiffViewStyle string `json:"diff_view_style"`
// Privacy
HideEmail bool `json:"hide_email"`
HideActivity bool `json:"hide_activity"`
}
// UserSettingsOptions represents options to change user settings
type UserSettingsOptions struct {
FullName *string `json:"full_name,omitempty"`
Website *string `json:"website,omitempty"`
Description *string `json:"description,omitempty"`
Location *string `json:"location,omitempty"`
Language *string `json:"language,omitempty"`
Theme *string `json:"theme,omitempty"`
DiffViewStyle *string `json:"diff_view_style,omitempty"`
// Privacy
HideEmail *bool `json:"hide_email,omitempty"`
HideActivity *bool `json:"hide_activity,omitempty"`
}
// GetUserSettings returns user settings
func (c *Client) GetUserSettings() (*UserSettings, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
userConfig := new(UserSettings)
resp, err := c.getParsedResponse("GET", "/user/settings", nil, nil, userConfig)
return userConfig, resp, err
}
// UpdateUserSettings returns user settings
func (c *Client) UpdateUserSettings(opt UserSettingsOptions) (*UserSettings, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_15_0); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, nil, err
}
userConfig := new(UserSettings)
resp, err := c.getParsedResponse("PATCH", "/user/settings", jsonHeader, bytes.NewReader(body), userConfig)
return userConfig, resp, err
}

View File

@ -45,6 +45,7 @@ var (
version1_12_0, _ = version.NewVersion("1.12.0") version1_12_0, _ = version.NewVersion("1.12.0")
version1_13_0, _ = version.NewVersion("1.13.0") version1_13_0, _ = version.NewVersion("1.13.0")
version1_14_0, _ = version.NewVersion("1.14.0") version1_14_0, _ = version.NewVersion("1.14.0")
version1_15_0, _ = version.NewVersion("1.15.0")
) )
// checkServerVersionGreaterThanOrEqual is internally used to speed up things and ignore issues with prerelease // checkServerVersionGreaterThanOrEqual is internally used to speed up things and ignore issues with prerelease

View File

@ -3,7 +3,7 @@
[![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey) [![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey)
[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://pkg.go.dev/github.com/AlecAivazis/survey/v2) [![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://pkg.go.dev/github.com/AlecAivazis/survey/v2)
A library for building interactive prompts. A library for building interactive and accessible prompts on terminals supporting ANSI escape sequences.
<img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/> <img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/>
@ -295,7 +295,7 @@ survey.AskOne(prompt, &color, survey.WithFilter(myFilter))
## Keeping the filter active ## Keeping the filter active
By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone. By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone.
However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect: However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect:
@ -342,11 +342,13 @@ survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
`survey` comes prepackaged with a few validators to fit common situations. Currently these `survey` comes prepackaged with a few validators to fit common situations. Currently these
validators include: validators include:
| name | valid types | description | notes | | name | valid types | description | notes |
| ------------ | ----------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------- | | ------------ | -------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response | | Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response |
| MinLength(n) | string | Enforces that a response is at least the given length | | | MinLength(n) | string | Enforces that a response is at least the given length | |
| MaxLength(n) | string | Enforces that a response is no longer than the given length | | | MaxLength(n) | string | Enforces that a response is no longer than the given length | |
| MaxItems(n) | []OptionAnswer | Enforces that a response has no more selections of the indicated | |
| MinItems(n) | []OptionAnswer | Enforces that a response has no less selections of the indicated | |
## Help Text ## Help Text
@ -456,6 +458,12 @@ For some examples, you can see any of the tests in this repo.
## FAQ ## FAQ
### What kinds of IO are supported by `survey`?
survey aims to support most terminal emulators; it expects support for ANSI escape sequences.
This means that reading from piped stdin or writing to piped stdout is **not supported**,
and likely to break your application in these situations. See [#337](https://github.com/AlecAivazis/survey/pull/337#issue-581351617)
### Why isn't sending a SIGINT (aka. CTRL-C) signal working? ### Why isn't sending a SIGINT (aka. CTRL-C) signal working?
When you send an interrupt signal to the process, it only interrupts the current prompt instead of the entire process. This manifests in a `github.com/AlecAivazis/survey/v2/terminal.InterruptErr` being returned from `Ask` and `AskOne`. If you want to stop the process, handle the returned error in your code: When you send an interrupt signal to the process, it only interrupts the current prompt instead of the entire process. This manifests in a `github.com/AlecAivazis/survey/v2/terminal.InterruptErr` being returned from `Ask` and `AskOne`. If you want to stop the process, handle the returned error in your code:

View File

@ -1,19 +1,19 @@
task "install-deps" { task "install-deps" {
description = "Install all of package dependencies" description = "Install all of package dependencies"
pipeline = [ pipeline = [
"go get {{.files}}", "go get -v {{.files}}",
] ]
} }
task "tests" { task "tests" {
description = "Run the test suite" description = "Run the test suite"
command = "go test {{.files}}" command = "go test -v {{.files}}"
environment { environment = {
GOFLAGS = "-mod=vendor" GOFLAGS = "-mod=vendor"
} }
} }
variables { variables = {
files = "$(go list -v ./... | grep -iEv \"tests|examples\")" files = "$(go list -v ./... | grep -iEv \"tests|examples\")"
} }

View File

@ -29,7 +29,7 @@ var TemplateFuncsNoColor = map[string]interface{}{
//for colored output. The second string does not contain escape codes //for colored output. The second string does not contain escape codes
//and can be used by the renderer for layout purposes. //and can be used by the renderer for layout purposes.
func RunTemplate(tmpl string, data interface{}) (string, string, error) { func RunTemplate(tmpl string, data interface{}) (string, string, error) {
tPair, err := getTemplatePair(tmpl) tPair, err := GetTemplatePair(tmpl)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -52,12 +52,12 @@ var (
memoMutex = &sync.RWMutex{} memoMutex = &sync.RWMutex{}
) )
//getTemplatePair returns a pair of compiled templates where the //GetTemplatePair returns a pair of compiled templates where the
//first template is generated for user-facing output and the //first template is generated for user-facing output and the
//second is generated for use by the renderer. The second //second is generated for use by the renderer. The second
//template does not contain any color escape codes, whereas //template does not contain any color escape codes, whereas
//the first template may or may not depending on DisableColor. //the first template may or may not depending on DisableColor.
func getTemplatePair(tmpl string) ([2]*template.Template, error) { func GetTemplatePair(tmpl string) ([2]*template.Template, error) {
memoMutex.RLock() memoMutex.RLock()
if t, ok := memoizedGetTemplate[tmpl]; ok { if t, ok := memoizedGetTemplate[tmpl]; ok {
memoMutex.RUnlock() memoMutex.RUnlock()

View File

@ -24,6 +24,11 @@ type OptionAnswer struct {
Index int Index int
} }
type reflectField struct {
value reflect.Value
fieldType reflect.StructField
}
func OptionAnswerList(incoming []string) []OptionAnswer { func OptionAnswerList(incoming []string) []OptionAnswer {
list := []OptionAnswer{} list := []OptionAnswer{}
for i, opt := range incoming { for i, opt := range incoming {
@ -63,13 +68,12 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) {
} }
// get the name of the field that matches the string we were given // get the name of the field that matches the string we were given
fieldIndex, err := findFieldIndex(elem, name) field, _, err := findField(elem, name)
// if something went wrong // if something went wrong
if err != nil { if err != nil {
// bubble up // bubble up
return err return err
} }
field := elem.Field(fieldIndex)
// handle references to the Settable interface aswell // handle references to the Settable interface aswell
if s, ok := field.Interface().(Settable); ok { if s, ok := field.Interface().(Settable); ok {
// use the interface method // use the interface method
@ -156,37 +160,51 @@ func IsFieldNotMatch(err error) (string, bool) {
// BUG(AlecAivazis): the current implementation might cause weird conflicts if there are // BUG(AlecAivazis): the current implementation might cause weird conflicts if there are
// two fields with same name that only differ by casing. // two fields with same name that only differ by casing.
func findFieldIndex(s reflect.Value, name string) (int, error) { func findField(s reflect.Value, name string) (reflect.Value, reflect.StructField, error) {
// the type of the value
sType := s.Type() fields := flattenFields(s)
// first look for matching tags so we can overwrite matching field names // first look for matching tags so we can overwrite matching field names
for i := 0; i < sType.NumField(); i++ { for _, f := range fields {
// the field we are current scanning
field := sType.Field(i)
// the value of the survey tag // the value of the survey tag
tag := field.Tag.Get(tagName) tag := f.fieldType.Tag.Get(tagName)
// if the tag matches the name we are looking for // if the tag matches the name we are looking for
if tag != "" && tag == name { if tag != "" && tag == name {
// then we found our index // then we found our index
return i, nil return f.value, f.fieldType, nil
} }
} }
// then look for matching names // then look for matching names
for i := 0; i < sType.NumField(); i++ { for _, f := range fields {
// the field we are current scanning
field := sType.Field(i)
// if the name of the field matches what we're looking for // if the name of the field matches what we're looking for
if strings.ToLower(field.Name) == strings.ToLower(name) { if strings.ToLower(f.fieldType.Name) == strings.ToLower(name) {
return i, nil return f.value, f.fieldType, nil
} }
} }
// we didn't find the field // we didn't find the field
return -1, errFieldNotMatch{name} return reflect.Value{}, reflect.StructField{}, errFieldNotMatch{name}
}
func flattenFields(s reflect.Value) []reflectField {
sType := s.Type()
numField := sType.NumField()
fields := make([]reflectField, 0, numField)
for i := 0; i < numField; i++ {
fieldType := sType.Field(i)
field := s.Field(i)
if field.Kind() == reflect.Struct && fieldType.Anonymous {
// field is a promoted structure
for _, f := range flattenFields(field) {
fields = append(fields, f)
}
continue
}
fields = append(fields, reflectField{field, fieldType})
}
return fields
} }
// isList returns true if the element is something we can Len() // isList returns true if the element is something we can Len()

View File

@ -11,9 +11,9 @@ require (
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.1 github.com/stretchr/testify v1.2.1
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1 // indirect golang.org/x/term v0.0.0-20210503060354-a79de5458b56
golang.org/x/text v0.3.0 golang.org/x/text v0.3.3
) )
go 1.13 go 1.13

View File

@ -25,7 +25,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1 h1:R4dVlxdmKenVdMRS/tTspEpSTRWINYrHD8ySIU9yCIU= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -1,6 +1,8 @@
package survey package survey
import ( import (
"errors"
"github.com/AlecAivazis/survey/v2/core" "github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal" "github.com/AlecAivazis/survey/v2/terminal"
) )
@ -19,8 +21,8 @@ type Input struct {
Default string Default string
Help string Help string
Suggest func(toComplete string) []string Suggest func(toComplete string) []string
typedAnswer string
answer string answer string
typedAnswer string
options []core.OptionAnswer options []core.OptionAnswer
selectedIndex int selectedIndex int
showingHelp bool showingHelp bool
@ -58,86 +60,90 @@ var InputQuestionTemplate = `
{{- if and .Suggest }}{{color "cyan"}}{{ print .Config.SuggestInput }} for suggestions{{end -}} {{- if and .Suggest }}{{color "cyan"}}{{ print .Config.SuggestInput }} for suggestions{{end -}}
]{{color "reset"}} {{end}} ]{{color "reset"}} {{end}}
{{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} {{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- .Answer -}}
{{- end}}` {{- end}}`
func (i *Input) OnChange(key rune, config *PromptConfig) (bool, error) { func (i *Input) onRune(config *PromptConfig) terminal.OnRuneFn {
if key == terminal.KeyEnter || key == '\n' { return terminal.OnRuneFn(func(key rune, line []rune) ([]rune, bool, error) {
if i.answer != config.HelpInput || i.Help == "" { if i.options != nil && (key == terminal.KeyEnter || key == '\n') {
// we're done return []rune(i.answer), true, nil
return true, nil } else if i.options != nil && key == terminal.KeyEscape {
} else {
i.answer = ""
i.showingHelp = true
}
} else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine {
i.answer = ""
} else if key == terminal.KeyEscape && i.Suggest != nil {
if len(i.options) > 0 {
i.answer = i.typedAnswer i.answer = i.typedAnswer
} i.options = nil
i.options = nil } else if key == terminal.KeyArrowUp && len(i.options) > 0 {
} else if key == terminal.KeyArrowUp && len(i.options) > 0 { if i.selectedIndex == 0 {
if i.selectedIndex == 0 { i.selectedIndex = len(i.options) - 1
i.selectedIndex = len(i.options) - 1 } else {
} else { i.selectedIndex--
i.selectedIndex-- }
} i.answer = i.options[i.selectedIndex].Value
i.answer = i.options[i.selectedIndex].Value } else if (key == terminal.KeyArrowDown || key == terminal.KeyTab) && len(i.options) > 0 {
} else if (key == terminal.KeyArrowDown || key == terminal.KeyTab) && len(i.options) > 0 { if i.selectedIndex == len(i.options)-1 {
if i.selectedIndex == len(i.options)-1 { i.selectedIndex = 0
} else {
i.selectedIndex++
}
i.answer = i.options[i.selectedIndex].Value
} else if key == terminal.KeyTab && i.Suggest != nil {
i.answer = string(line)
i.typedAnswer = i.answer
options := i.Suggest(i.answer)
i.selectedIndex = 0 i.selectedIndex = 0
} else { if len(options) == 0 {
i.selectedIndex++ return line, false, nil
} }
i.answer = i.options[i.selectedIndex].Value
} else if key == terminal.KeyTab && i.Suggest != nil {
options := i.Suggest(i.answer)
i.selectedIndex = 0
i.typedAnswer = i.answer
if len(options) > 0 {
i.answer = options[0] i.answer = options[0]
if len(options) == 1 { if len(options) == 1 {
i.typedAnswer = i.answer
i.options = nil i.options = nil
} else { } else {
i.options = core.OptionAnswerList(options) i.options = core.OptionAnswerList(options)
} }
} } else {
} else if key == terminal.KeyDelete || key == terminal.KeyBackspace { if i.options == nil {
if i.answer != "" { return line, false, nil
runeAnswer := []rune(i.answer) }
i.answer = string(runeAnswer[0 : len(runeAnswer)-1])
}
} else if key >= terminal.KeySpace {
i.answer += string(key)
i.typedAnswer = i.answer
i.options = nil
}
pageSize := config.PageSize if key >= terminal.KeySpace {
opts, idx := paginate(pageSize, i.options, i.selectedIndex) i.answer += string(key)
err := i.Render( }
InputQuestionTemplate, i.typedAnswer = i.answer
InputTemplateData{
Input: *i,
Answer: i.answer,
ShowHelp: i.showingHelp,
SelectedIndex: idx,
PageEntries: opts,
Config: config,
},
)
return err != nil, err i.options = nil
}
pageSize := config.PageSize
opts, idx := paginate(pageSize, i.options, i.selectedIndex)
err := i.Render(
InputQuestionTemplate,
InputTemplateData{
Input: *i,
Answer: i.answer,
ShowHelp: i.showingHelp,
SelectedIndex: idx,
PageEntries: opts,
Config: config,
},
)
if err == nil {
err = readLineAgain
}
return []rune(i.typedAnswer), true, err
})
} }
var readLineAgain = errors.New("read line again")
func (i *Input) Prompt(config *PromptConfig) (interface{}, error) { func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
// render the template // render the template
err := i.Render( err := i.Render(
InputQuestionTemplate, InputQuestionTemplate,
InputTemplateData{ InputTemplateData{
Input: *i, Input: *i,
Config: config, Config: config,
ShowHelp: i.showingHelp,
}, },
) )
if err != nil { if err != nil {
@ -150,30 +156,39 @@ func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
defer rr.RestoreTermMode() defer rr.RestoreTermMode()
cursor := i.NewCursor() cursor := i.NewCursor()
cursor.Hide() // hide the cursor if !config.ShowCursor {
defer cursor.Show() // show the cursor when we're done cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done
}
var line []rune
// start waiting for input
for { for {
r, _, err := rr.ReadRune() if i.options != nil {
if err != nil { line = []rune{}
return "", err }
}
if r == terminal.KeyInterrupt { line, err = rr.ReadLineWithDefault(0, line, i.onRune(config))
return "", terminal.InterruptErr if err == readLineAgain {
} continue
if r == terminal.KeyEndTransmission {
break
} }
b, err := i.OnChange(r, config)
if err != nil { if err != nil {
return "", err return "", err
} }
if b { break
break }
}
i.answer = string(line)
// readline print an empty line, go up before we render the follow up
cursor.Up(1)
// if we ran into the help string
if i.answer == config.HelpInput && i.Help != "" {
// show the help and prompt again
i.showingHelp = true
return i.Prompt(config)
} }
// if the line is empty // if the line is empty

View File

@ -45,9 +45,27 @@ type MultiSelectTemplateData struct {
ShowHelp bool ShowHelp bool
PageEntries []core.OptionAnswer PageEntries []core.OptionAnswer
Config *PromptConfig Config *PromptConfig
// These fields are used when rendering an individual option
CurrentOpt core.OptionAnswer
CurrentIndex int
}
// IterateOption sets CurrentOpt and CurrentIndex appropriately so a multiselect option can be rendered individually
func (m MultiSelectTemplateData) IterateOption(ix int, opt core.OptionAnswer) interface{} {
copy := m
copy.CurrentIndex = ix
copy.CurrentOpt = opt
return copy
} }
var MultiSelectQuestionTemplate = ` var MultiSelectQuestionTemplate = `
{{- define "option"}}
{{- if eq .SelectedIndex .CurrentIndex }}{{color .Config.Icons.SelectFocus.Format }}{{ .Config.Icons.SelectFocus.Text }}{{color "reset"}}{{else}} {{end}}
{{- if index .Checked .CurrentOpt.Index }}{{color .Config.Icons.MarkedOption.Format }} {{ .Config.Icons.MarkedOption.Text }} {{else}}{{color .Config.Icons.UnmarkedOption.Format }} {{ .Config.Icons.UnmarkedOption.Text }} {{end}}
{{- color "reset"}}
{{- " "}}{{- .CurrentOpt.Value}}
{{end}}
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} {{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} {{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}} {{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
@ -56,10 +74,7 @@ var MultiSelectQuestionTemplate = `
{{- " "}}{{- color "cyan"}}[Use arrows to move, space to select, <right> to all, <left> to none, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}} {{- " "}}{{- color "cyan"}}[Use arrows to move, space to select, <right> to all, <left> to none, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
{{- "\n"}} {{- "\n"}}
{{- range $ix, $option := .PageEntries}} {{- range $ix, $option := .PageEntries}}
{{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }}{{color "reset"}}{{else}} {{end}} {{- template "option" $.IterateOption $ix $option}}
{{- if index $.Checked $option.Index }}{{color $.Config.Icons.MarkedOption.Format }} {{ $.Config.Icons.MarkedOption.Text }} {{else}}{{color $.Config.Icons.UnmarkedOption.Format }} {{ $.Config.Icons.UnmarkedOption.Text }} {{end}}
{{- color "reset"}}
{{- " "}}{{$option.Value}}{{"\n"}}
{{- end}} {{- end}}
{{- end}}` {{- end}}`
@ -159,18 +174,17 @@ func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
// and we have modified the filter then we should move the page back! // and we have modified the filter then we should move the page back!
opts, idx := paginate(pageSize, options, m.selectedIndex) opts, idx := paginate(pageSize, options, m.selectedIndex)
tmplData := MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
ShowHelp: m.showingHelp,
PageEntries: opts,
Config: config,
}
// render the options // render the options
m.Render( m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx)
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
ShowHelp: m.showingHelp,
PageEntries: opts,
Config: config,
},
)
} }
func (m *MultiSelect) filterOptions(config *PromptConfig) []core.OptionAnswer { func (m *MultiSelect) filterOptions(config *PromptConfig) []core.OptionAnswer {
@ -250,20 +264,21 @@ func (m *MultiSelect) Prompt(config *PromptConfig) (interface{}, error) {
opts, idx := paginate(pageSize, core.OptionAnswerList(m.Options), m.selectedIndex) opts, idx := paginate(pageSize, core.OptionAnswerList(m.Options), m.selectedIndex)
cursor := m.NewCursor() cursor := m.NewCursor()
cursor.Hide() // hide the cursor cursor.Save() // for proper cursor placement during selection
defer cursor.Show() // show the cursor when we're done cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done
defer cursor.Restore() // clear any accessibility offsetting on exit
tmplData := MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
PageEntries: opts,
Config: config,
}
// ask the question // ask the question
err := m.Render( err := m.RenderWithCursorOffset(MultiSelectQuestionTemplate, tmplData, opts, idx)
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
PageEntries: opts,
Config: config,
},
)
if err != nil { if err != nil {
return "", err return "", err
} }

Some files were not shown because too many files have changed in this diff Show More