40 Commits

Author SHA1 Message Date
a979df1070 Changelog v0.4.0 (#157)
Changelog for v0.4.0

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/157
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: appleboy <appleboy.tw@gmail.com>
2020-07-18 10:57:18 +00:00
08a9a9a8ab Bugfixes for initCommand (#156)
respect --login parameter again

🚀

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/156
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
2020-07-18 10:43:56 +00:00
2135af0304 Fix Login Detection By Repo Param (#151)
Fix login detection by Repo param

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/151
Reviewed-by: lafriks <lafriks@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-07-17 16:36:48 +00:00
19ee32168e Add notifications subcomand (#148)
rm Aliase news for now

show read notifications not supported jet

add notifications subcomand

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/148
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: mrsdizzie <info@mrsdizzie.com>
2020-07-17 15:30:02 +00:00
12ea1ad35c Subcomand Login Show List By Default (#152)
tea login list by default

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/152
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: lafriks <lafriks@noreply.gitea.io>
2020-07-17 07:16:38 +00:00
3a382e73b1 Fix Login List Output (#150)
fix output

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/150
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
2020-07-16 16:12:22 +00:00
f1801f39a6 Add subcomand 'pulls create' (#144)
wordings

print pull URL

change title required mesage

inform User on push error

fix body

🚀

finish

impruve

fix help menue of pull create

refactor

Add pull-request command

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: Sandro Santilli <strk@kbt.io>
Reviewed-on: https://gitea.com/gitea/tea/pulls/144
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: lafriks <lafriks@noreply.gitea.io>
2020-07-16 15:00:51 +00:00
66947bcf09 Update Vendors (#145)
vendor

go update vendors

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/145
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
2020-07-13 22:38:05 +00:00
85e1244db8 Add note for alpine/arch-linux package (#143)
add note for archlinux TEA package

add note for alpine linux TEA package

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/143
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
2020-07-05 18:36:21 +00:00
618eeb9029 Add Changelog for v0.3.1 (#139) (#140)
Add Changelog for v0.3.1 (#139)

Add Changelog for v0.3.1

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/139
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>

Reviewed-on: https://gitea.com/gitea/tea/pulls/140
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
2020-06-15 20:17:45 +00:00
b2efb45f64 --ssh-key should be string not bool (#135)
--ssh-key should be string not bool

Fix #134

Reviewed-on: https://gitea.com/gitea/tea/pulls/135
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-06-12 02:43:55 +00:00
59fe58577a Migrate gitea-sdk to v0.12.0 (#133)
Migrate

Update code.gitea.io/sdk/gitea to v0.12.0.

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/133
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-05-27 13:48:09 +00:00
3c312cb409 Update Vendors: (#129)
* github.com/araddon/dateparse upgrade => v0.0.0-20200409225146-d820a6159ab1
* code.gitea.io/sdk/gitea upgrade => v0.11.3
* github.com/olekukonko/tablewriter upgrade => v0.0.4
* github.com/mattn/go-runewidth upgrade => v0.0.9
* github.com/stretchr/testify upgrade => v1.5.1
* github.com/davecgh/go-spew upgrade => v1.1.1
* github.com/urfave/cli/v2 upgrade => v2.2.0

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/129
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-04-30 10:54:11 +00:00
6cff3b1cc7 migrate src-d/go-git -> go-git/go-git (#128)
Merge branch 'master' into vendor-migrate-go-git

Merge branch 'master' into vendor-migrate-go-git

migrate src-d/go-git -> go-git/go-git

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/128
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-04-30 02:02:15 +00:00
4b059770ea Add gitea-vet (#121)
Close #119

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: jolheiser <john.olheiser@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/121
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
2020-04-28 13:02:21 +00:00
a35fcb682b migrate yaml lib (#130)
vendor

migrate imports

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/130
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
2020-04-28 03:39:36 +00:00
8bbeeae327 Update Readme & add demo GIF (#125)
add suggestions from reviews & remove alias from subcomand list

gif is now on cdn & add review suggestion

Add ICON & Name

update description & co

add demo gif

fix newline becaues of new markdown roule

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/125
Reviewed-by: lafriks <lafriks@noreply.gitea.io>
Reviewed-by: Gary Kim <gary@garykim.dev>
2020-04-25 12:04:05 +00:00
35ad046d8d modules/git: fix dropped error (#127)
modules/git: fix dropped error

Co-authored-by: Lars Lehtonen <lars.lehtonen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/127
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-04-25 02:35:22 +00:00
e9dee07459 Issues details: add missing newline (#126)
add missing newline

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/126
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: mrsdizzie <info@mrsdizzie.com>
2020-04-23 00:28:54 +00:00
2653eaa6b2 Changelog for v0.3.0 (#122)
remove version info in same way it is done on gitea

Add Changelog for v0.3.0

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/122
Reviewed-by: mrsdizzie <info@mrsdizzie.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
2020-04-22 19:11:03 +00:00
1a4d8edf37 add tea issues [open|close] commands (#99)
update to master

Merge branch 'master' into issue-openclose

adress code review

add issues [open|close]

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/99
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
2020-04-22 16:09:26 +00:00
4cda7e0299 add tea pulls [checkout | clean] commands (#93 #97 #107) (#105)
Merge branch 'master' into issue-97/pulls-clean

vendor terminal dependency

pull/push: provide authentication method

automatically select an AuthMethod according to the
remote url type. If required, credentials are prompted for

login: store username & optional keyfile

refactor

refactor GetRemote

Merge branch 'master' into issue-97/pulls-clean

adress code review

add --ignore-sha flag

When set, the local branch is not matched against the remote sha,
but the remote branch name. This makes the command more flexible
with diverging branches.

add missing error check

fix branch-not-found case

Merge branch 'master' into issue-97/pulls-clean

use directory namespaces for branches & remotes

fix TeaCreateBranch()

improve method of TeaFindBranch()

now only checking .git/refs instead of looking up .git/config which may
not list the branch

add `tea pulls clean`

fixes #97

add copyright to new files

make linter happy

refactor: use new git functions for old code

add `tea pulls checkout`

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/105
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-04-19 03:09:03 +00:00
81fa4e78ff Fix CI: Turn off go modules for xgo and gxz (#115)
Turn off go modules for xgo and gxz

as on https://github.com/go-gitea/gitea/pull/10963

Revert "Fix CI: workaround from @kolaente (#113)"

This reverts commit b37673c954.

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/115
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
2020-04-06 14:21:36 +00:00
08a00add6d ignore PRs in runIssuesList() (#111)
Merge branch 'master' into 108-issues

Merge branch 'master' into 108-issues

ignore pullrequests

Update SDK v0.11.0 -> 0.11.2

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/111
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-04-01 08:35:42 +00:00
7a10ea10df Add tea open (#101)
Fix open default to home page

Improve path join and open with no arg

add labels and milestones

Add tea open

Reviewed-on: https://gitea.com/gitea/tea/pulls/101
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
2020-04-01 03:22:24 +00:00
b37673c954 Fix CI: workaround from @kolaente (#113)
as workaround from @kolaente

Signed-off-by: 6543 <6543@obermui.de>

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/113
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
2020-03-31 18:11:14 +00:00
f5d0790619 Add --state flag filter to issue & PR lists (#100)
Add --state flag filter to issue & PR lists

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin Roosen <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/100
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: 6543 <6543@noreply.gitea.io>
2020-03-29 13:16:06 +00:00
12f38c892f Fix CI for releasing tea (#106)
fix ci

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/106
Reviewed-by: mrsdizzie <info@mrsdizzie.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-03-08 01:29:46 +00:00
31a79f0605 Changelog v0.2.0 (#102)
Changelog v0.2.0

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/102
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-03-07 01:24:51 +00:00
897e4ce3c1 add tea times command (#54)
Merge branch 'master' into 50-cmd-times

labels: fix refactor bug

fixup! use version check implemented in SDK instead

add subcmds: `tea times (delete|reset)`

fixes #87
fixes #88

times: reword help

use version check implemented in SDK instead

make fmt

Check gitea server version for times endpoint

refactor times.go

dont print TrackedTime ID

print username & issue index instead of IDs

switch to urface/cli/v2

vendor araddon/dateparse

use araddon/dateparse for arbitrary date inputs

add --from, --until flags

allow filtering by issue index

make app name lower case

to make the help texts consistent with the binary name

add --total flag

implement `tea times add`

add `tea times` subcommand

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin Roosen <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/54
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-03-06 03:43:28 +00:00
5601c57059 Improve makefile to enable goproxy when go get tools. (#98)
makefile go mod

always enable GO111MODULE=on

Reviewed-on: https://gitea.com/gitea/tea/pulls/98
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: Andrew Thornton <art27@cantab.net>
2020-03-05 09:22:41 +00:00
c8cbd6b0ee bump code.gitea.io/sdk/gitea from untaged to 0.11.0 (#92)
bump code.gitea.io/sdk/gitea from untaged to 0.11.0

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/92
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
2020-02-07 13:53:15 +00:00
8d61d8beec Add missing Changelog for 0.1.x releases (#91)
add Changelog

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/91
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
2020-01-31 02:24:57 +00:00
c20d7d45aa chore(cli): upgrade urfave/cli to v2 version (#85)
chore(cli): upgrade urfave/cli to v2 version

Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
2020-01-04 17:44:25 +00:00
0a5cdd60ac [Vendor] Update urfave/cli v1.20.0 -> v1.22.2 (#84)
Merge branch 'master' into update-cli-lib2

[Vendor] Update stretchr/testify  v1.3.0 -> v1.4.0 (#83)

update github.com/stretchr/testify  v1.3.0 -> v1.4.0

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: sapk <sapk@noreply.gitea.io>
Reviewed-by: appleboy <appleboy.tw@gmail.com>

Update urfave/cli v1.20.0 -> v1.22.2

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-by: mrsdizzie <info@mrsdizzie.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-01-04 04:45:36 +00:00
bffed6b4d3 [Vendor] Update stretchr/testify v1.3.0 -> v1.4.0 (#83)
update github.com/stretchr/testify  v1.3.0 -> v1.4.0

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: sapk <sapk@noreply.gitea.io>
Reviewed-by: appleboy <appleboy.tw@gmail.com>
2020-01-04 02:49:49 +00:00
3c66a7af24 [Vendor] Update gitea go-sdk to 2020-01-03 (#81)
update sdk to v0.0.0-20200103062250-c7686bd633c6

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: sapk <sapk@noreply.gitea.io>
Reviewed-by: mrsdizzie <info@mrsdizzie.com>
2020-01-04 01:44:33 +00:00
6e85b47f7c README: add badges (#80)
use SVG instead of PNG

update discord link

add badges

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-by: sapk <sapk@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-01-03 00:58:45 +00:00
d9e6db96a7 README: document some dev tasks (#79) 2019-12-26 03:26:48 +00:00
97f0ea1c22 Add --remote flag to add/create subcommands (#77) 2019-12-18 11:21:29 +00:00
762 changed files with 53515 additions and 97063 deletions

56
.changelog.yml Executable file
View File

@ -0,0 +1,56 @@
# The full repository name
repo: gitea/tea
# Service type (gitea or github)
service: gitea
# Base URL for Gitea instance if using gitea service type (optional)
base-url: https://gitea.com
# Changelog groups and which labeled PRs to add to each group
groups:
-
name: BREAKING
labels:
- kind/breaking
-
name: FEATURES
labels:
- kind/feature
-
name: BUGFIXES
labels:
- kind/bug
-
name: ENHANCEMENTS
labels:
- kind/enhancement
- kind/refactor
- kind/ui
-
name: SECURITY
labels:
- kind/security
-
name: TESTING
labels:
- kind/testing
-
name: TRANSLATION
labels:
- kind/translation
-
name: BUILD
labels:
- kind/build
- kind/lint
-
name: DOCS
labels:
- kind/docs
-
name: MISC
default: true
# regex indicating which labels to skip for the changelog
skip-labels: skip-changelog|backport\/.+

View File

@ -72,6 +72,8 @@ steps:
- name: static - name: static
pull: always pull: always
image: techknowlogick/xgo:latest image: techknowlogick/xgo:latest
environment:
GOPROXY: https://goproxy.cn
commands: commands:
- export PATH=$PATH:$GOPATH/bin - export PATH=$PATH:$GOPATH/bin
- make release - make release

2
.gitignore vendored
View File

@ -1,4 +1,6 @@
tea tea
/gitea-vet
.idea/ .idea/
.history/ .history/
dist/ dist/

74
CHANGELOG.md Normal file
View File

@ -0,0 +1,74 @@
# Changelog
## [v0.4.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1264) - 2020-07-18
* FEATURES
* Add notifications subcomand (#148)
* Add subcomand 'pulls create' (#144)
* BUGFIXES
* Fix Login Detection By Repo Param (#151)
* Fix Login List Output (#150)
* Fix --ssh-key Option (#135)
* ENHANCEMENTS
* Subcomand Login Show List By Default (#152)
* BUILD
* Migrate src-d/go-git to go-git/go-git (#128)
* Migrate gitea-sdk to v0.12.0 (#133)
* Migrate yaml lib (#130)
* Add gitea-vet (#121)
## [v0.3.1](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1265) - 2020-06-15
* BUGFIXES
* --ssh-key should be string not bool (#135) (#137)
* modules/git: fix dropped error (#127)
* Issues details: add missing newline (#126)
## [v0.3.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1227) - 2020-04-22
* FEATURES
* Add `tea pulls [checkout | clean]` commands (#93 #97 #107) (#105)
* Add `tea open` (#101)
* Add `tea issues [open|close]` commands (#99)
* ENHANCEMENTS
* Ignore PRs for `tea issues` (#111)
* Add --state flag filter to issue & PR lists (#100)
## [v0.2.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=538) - 2020-03-06
* FEATURES
* Add `tea times` command (#54)
* ENHANCEMENTS
* Upgrade urfave/cli to v2 version (#85)
* Add --remote flag to add/create subcommands (#77)
* BUILD
* Upgrade gitea/go-sdk to 2020-01-03 (#81)
* Update stretchr/testify v1.3.0 -> v1.4.0 (#83)
* Improve makefile to enable goproxy when go get tools (#98)
## [v0.1.2](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-11-15
* BUILD
* Fix typo in drone (#75)
## [v0.1.1](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-11-15
* FEATURES
* Add repos subcommand (#65)
* ENHANCEMENTS
* Minor improvements to command-line language (#66)
## [v0.1.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-10-28
* BREAKING
* Changed git config determination to go-git (#41) [continue #45] (#62)
* FEATURES
* Add labels commands (#36)
* BUGFIXES
* Fix out -o flag (#53)
* Fix log formatting, refactor flag definition in cmd/labels.go (#52)
* ENHANCEMENTS
* List label description (#60)
* Use Different Remote Repos (#58)
* Unified output (#14) (#40)
* Added global appendable Flags (#12) (#39)
* BUILD
* Change .drone.yml to new format (#33)
* DOCS
* Add install guide from brew on README (#61)

View File

@ -1,6 +1,6 @@
DIST := dist DIST := dist
IMPORT := code.gitea.io/tea IMPORT := code.gitea.io/tea
export GO111MODULE=off export GO111MODULE=on
GO ?= go GO ?= go
SED_INPLACE := sed -i SED_INPLACE := sed -i
@ -59,7 +59,7 @@ all: build
.PHONY: clean .PHONY: clean
clean: clean:
$(GO) clean -i ./... $(GO) clean -mod=vendor -i ./...
rm -rf $(EXECUTABLE) $(DIST) rm -rf $(EXECUTABLE) $(DIST)
.PHONY: fmt .PHONY: fmt
@ -68,26 +68,30 @@ fmt:
.PHONY: vet .PHONY: vet
vet: vet:
$(GO) vet $(PACKAGES) # Default vet
$(GO) vet -mod=vendor $(PACKAGES)
# Custom vet
$(GO) build -mod=vendor gitea.com/jolheiser/gitea-vet
$(GO) vet -vettool=gitea-vet $(PACKAGES)
.PHONY: lint .PHONY: lint
lint: lint:
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/mgechev/revive; \ cd /tmp && $(GO) get -u github.com/mgechev/revive; \
fi fi
revive -config .revive.toml -exclude=./vendor/... ./... || exit 1 revive -config .revive.toml -exclude=./vendor/... ./... || exit 1
.PHONY: misspell-check .PHONY: misspell-check
misspell-check: misspell-check:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \ cd /tmp && $(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi fi
misspell -error -i unknwon,destory $(GOFILES) misspell -error -i unknwon,destory $(GOFILES)
.PHONY: misspell .PHONY: misspell
misspell: misspell:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \ cd /tmp && $(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi fi
misspell -w -i unknwon $(GOFILES) misspell -w -i unknwon $(GOFILES)
@ -103,15 +107,15 @@ fmt-check:
.PHONY: test .PHONY: test
test: test:
GO111MODULE=on $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' $(PACKAGES) $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' $(PACKAGES)
.PHONY: unit-test-coverage .PHONY: unit-test-coverage
unit-test-coverage: unit-test-coverage:
$(GO) test -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: vendor .PHONY: vendor
vendor: vendor:
GO111MODULE=on $(GO) mod tidy && GO111MODULE=on $(GO) mod vendor $(GO) mod tidy && $(GO) mod vendor
.PHONY: test-vendor .PHONY: test-vendor
test-vendor: vendor test-vendor: vendor
@ -133,7 +137,7 @@ install: $(wildcard *.go)
build: $(EXECUTABLE) build: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES) $(EXECUTABLE): $(SOURCES)
GO111MODULE=on $(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: release .PHONY: release
release: release-dirs release-windows release-linux release-darwin release-copy release-compress release-check release: release-dirs release-windows release-linux release-darwin release-copy release-compress release-check
@ -145,9 +149,9 @@ release-dirs:
.PHONY: release-windows .PHONY: release-windows
release-windows: release-windows:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \ cd /tmp && $(GO) get -u src.techknowlogick.com/xgo; \
fi fi
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out tea-$(VERSION) . GO111MODULE=off xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out tea-$(VERSION) .
ifeq ($(CI),drone) ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries cp /build/* $(DIST)/binaries
endif endif
@ -155,9 +159,9 @@ endif
.PHONY: release-linux .PHONY: release-linux
release-linux: release-linux:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \ cd /tmp && $(GO) get -u src.techknowlogick.com/xgo; \
fi fi
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out tea-$(VERSION) . GO111MODULE=off xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out tea-$(VERSION) .
ifeq ($(CI),drone) ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries cp /build/* $(DIST)/binaries
endif endif
@ -165,9 +169,9 @@ endif
.PHONY: release-darwin .PHONY: release-darwin
release-darwin: release-darwin:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \ cd /tmp && $(GO) get -u src.techknowlogick.com/xgo; \
fi fi
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out tea-$(VERSION) . GO111MODULE=off xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out tea-$(VERSION) .
ifeq ($(CI),drone) ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries cp /build/* $(DIST)/binaries
endif endif
@ -179,7 +183,7 @@ release-copy:
.PHONY: release-compress .PHONY: release-compress
release-compress: release-compress:
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \ GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
fi fi
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done; cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;

View File

@ -1,31 +1,35 @@
# Gitea Command Line Tool for Go # <img alt='' src='https://gitea.com/repo-avatars/550-80a3a8c2ab0e2c2d69f296b7f8582485' height="40"/> *T E A*
This project acts as a command line tool for operating one or multiple Gitea instances. It depends on [code.gitea.io/sdk](https://code.gitea.io/sdk) client SDK implementation written in Go to interact with [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Release](https://raster.shields.io/badge/dynamic/json.svg?label=release&url=https://gitea.com/api/v1/repos/gitea/tea/releases&query=$[0].tag_name)](https://gitea.com/gitea/tea/releases) [![Build Status](https://drone.gitea.com/api/badges/gitea/tea/status.svg)](https://drone.gitea.com/gitea/tea) [![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea) [![Go Report Card](https://goreportcard.com/badge/code.gitea.io/tea)](https://goreportcard.com/report/code.gitea.io/tea) [![GoDoc](https://godoc.org/code.gitea.io/tea?status.svg)](https://godoc.org/code.gitea.io/tea)
the Gitea API implementation.
## The official CLI interface for gitea
Tea is a command line tool for interacting on one or more Gitea instances.
It uses [code.gitea.io/sdk](https://code.gitea.io/sdk) and interacts with the Gitea API
![demo gif](https://dl.gitea.io/screenshots/tea_demo.gif)
## Installation ## Installation
Currently no prebuilt binaries are provided. You can use the prebuilt binaries from [dl.gitea.io](https://dl.gitea.io/tea/)
To install, a Go installation is needed.
To install from source, go 1.12 or newer is required:
```sh ```sh
go get code.gitea.io/tea go get code.gitea.io/tea
go install code.gitea.io/tea go install code.gitea.io/tea
``` ```
If the `tea` executable is not found, you might need to set up your `$GOPATH` and `$PATH` variables first:
```sh If you have `brew` installed, you can install `tea` via:
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
```
If you have `brew` installed, you can install tea version via:
```sh ```sh
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
brew install --devel tea brew install tea
``` ```
Distribution packages exist for: **alpinelinux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge))** and **archlinux ([gitea-tea](https://aur.archlinux.org/packages/gitea-tea))**
## Usage ## Usage
First of all, you have to create a token on your `personal settings -> application` page of your gitea instance. First of all, you have to create a token on your `personal settings -> application` page of your gitea instance.
@ -35,29 +39,42 @@ Use this token to login with `tea`:
tea login add --name=try --url=https://try.gitea.io --token=xxxxxx tea login add --name=try --url=https://try.gitea.io --token=xxxxxx
``` ```
Now you can use the `tea` commands: Now you can use the following `tea` subcommands.
Detailed usage information is available via `tea <command> --help`.
```sh ```sh
tea issues login Log in to a Gitea server
tea releases logout Log out from a Gitea server
issues List, create and update issues
pulls List, checkout and clean pull requests
releases Create releases
repos Operate with repositories
labels Manage issue labels
times Operate on tracked times of a repositorys issues and pulls
open Open something of the repository on web browser
``` ```
> If you are inside a git repository hosted on a gitea instance, you don't need to specify the `--login` and `--repo` flags! To fetch issues from different repos, use the `--remote` flag (when inside a gitea repository directory) or `--login` & `--repo` flags.
## Compilation ## Compilation
Make sure you have installed a current go version.
To compile the sources yourself run the following: To compile the sources yourself run the following:
```sh ```sh
go get code.gitea.io/tea git clone https://gitea.com/gitea/tea.git
cd "${GOPATH}/src/code.gitea.io/tea" cd tea
go build make
``` ```
## Contributing ## Contributing
Fork -> Patch -> Push -> Pull Request Fork -> Patch -> Push -> Pull Request
- `make test` run testsuite
- `make vendor` when adding new dependencies
- ... (for other development tasks, check the `Makefile`)
## Authors ## Authors
* [Maintainers](https://github.com/orgs/go-gitea/people) * [Maintainers](https://github.com/orgs/go-gitea/people)

14
build.go Normal file
View File

@ -0,0 +1,14 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
//+build vendor
package main
// Libraries that are included to vendor utilities used during build.
// These libraries will not be included in a normal compilation.
import (
// for vet
_ "gitea.com/jolheiser/gitea-vet"
)

View File

@ -18,21 +18,24 @@ import (
"strings" "strings"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
local_git "code.gitea.io/tea/modules/git" "code.gitea.io/tea/modules/git"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
go_git "gopkg.in/src-d/go-git.v4"
"github.com/go-gitea/yaml" "gopkg.in/yaml.v2"
) )
// Login represents a login to a gitea server, you even could add multiple logins for one gitea server // Login represents a login to a gitea server, you even could add multiple logins for one gitea server
type Login struct { type Login struct {
Name string `yaml:"name"` Name string `yaml:"name"`
URL string `yaml:"url"` URL string `yaml:"url"`
Token string `yaml:"token"` Token string `yaml:"token"`
Active bool `yaml:"active"` Active bool `yaml:"active"`
SSHHost string `yaml:"ssh_host"` SSHHost string `yaml:"ssh_host"`
// optional path to the private key
SSHKey string `yaml:"ssh_key"`
Insecure bool `yaml:"insecure"` Insecure bool `yaml:"insecure"`
// optional gitea username
User string `yaml:"user"`
} }
// Client returns a client to operate Gitea API // Client returns a client to operate Gitea API
@ -186,12 +189,15 @@ func saveConfig(ymlPath string) error {
return ioutil.WriteFile(ymlPath, bs, 0660) return ioutil.WriteFile(ymlPath, bs, 0660)
} }
func curGitRepoPath() (*Login, string, error) { func curGitRepoPath(path string) (*Login, string, error) {
gitPath, err := go_git.PlainOpenWithOptions("./", &go_git.PlainOpenOptions{DetectDotGit: true}) var err error
if err != nil { var repo *git.TeaRepo
return nil, "", errors.New("No Gitea login found") if len(path) == 0 {
repo, err = git.RepoForWorkdir()
} else {
repo, err = git.RepoFromPath(path)
} }
gitConfig, err := gitPath.Config() gitConfig, err := repo.Config()
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
@ -224,7 +230,7 @@ func curGitRepoPath() (*Login, string, error) {
for _, l := range config.Logins { for _, l := range config.Logins {
for _, u := range remoteConfig.URLs { for _, u := range remoteConfig.URLs {
p, err := local_git.ParseURL(strings.TrimSpace(u)) p, err := git.ParseURL(strings.TrimSpace(u))
if err != nil { if err != nil {
return nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error()) return nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error())
} }

View File

@ -7,7 +7,7 @@ package cmd
import ( import (
"log" "log"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// create global variables for global Flags to simplify // create global variables for global Flags to simplify
@ -21,28 +21,32 @@ var (
// LoginFlag provides flag to specify tea login profile // LoginFlag provides flag to specify tea login profile
var LoginFlag = cli.StringFlag{ var LoginFlag = cli.StringFlag{
Name: "login, l", Name: "login",
Aliases: []string{"l"},
Usage: "Use a different Gitea login. Optional", Usage: "Use a different Gitea login. Optional",
Destination: &loginValue, Destination: &loginValue,
} }
// RepoFlag provides flag to specify repository // RepoFlag provides flag to specify repository
var RepoFlag = cli.StringFlag{ var RepoFlag = cli.StringFlag{
Name: "repo, r", Name: "repo",
Aliases: []string{"r"},
Usage: "Repository to interact with. Optional", Usage: "Repository to interact with. Optional",
Destination: &repoValue, Destination: &repoValue,
} }
// RemoteFlag provides flag to specify remote repository // RemoteFlag provides flag to specify remote repository
var RemoteFlag = cli.StringFlag{ var RemoteFlag = cli.StringFlag{
Name: "remote, R", Name: "remote",
Aliases: []string{"R"},
Usage: "Discover Gitea login from remote. Optional", Usage: "Discover Gitea login from remote. Optional",
Destination: &remoteValue, Destination: &remoteValue,
} }
// OutputFlag provides flag to specify output type // OutputFlag provides flag to specify output type
var OutputFlag = cli.StringFlag{ var OutputFlag = cli.StringFlag{
Name: "output, o", Name: "output",
Aliases: []string{"o"},
Usage: "Output format. (csv, simple, table, tsv, yaml)", Usage: "Output format. (csv, simple, table, tsv, yaml)",
Destination: &outputValue, Destination: &outputValue,
} }
@ -52,8 +56,8 @@ var OutputFlag = cli.StringFlag{
// subcommand to work around issue and provide --login and --output: // subcommand to work around issue and provide --login and --output:
// https://github.com/urfave/cli/issues/585 // https://github.com/urfave/cli/issues/585
var LoginOutputFlags = []cli.Flag{ var LoginOutputFlags = []cli.Flag{
LoginFlag, &LoginFlag,
OutputFlag, &OutputFlag,
} }
// LoginRepoFlags defines login and repo flags that should // LoginRepoFlags defines login and repo flags that should
@ -61,8 +65,9 @@ var LoginOutputFlags = []cli.Flag{
// the subcommand to work around issue and provide --login and --repo: // the subcommand to work around issue and provide --login and --repo:
// https://github.com/urfave/cli/issues/585 // https://github.com/urfave/cli/issues/585
var LoginRepoFlags = []cli.Flag{ var LoginRepoFlags = []cli.Flag{
LoginFlag, &LoginFlag,
RepoFlag, &RepoFlag,
&RemoteFlag,
} }
// AllDefaultFlags defines flags that should be available // AllDefaultFlags defines flags that should be available
@ -70,38 +75,29 @@ var LoginRepoFlags = []cli.Flag{
// to work around issue and provide --login, --repo and --output: // to work around issue and provide --login, --repo and --output:
// https://github.com/urfave/cli/issues/585 // https://github.com/urfave/cli/issues/585
var AllDefaultFlags = append([]cli.Flag{ var AllDefaultFlags = append([]cli.Flag{
RepoFlag, &RepoFlag,
RemoteFlag, &RemoteFlag,
}, LoginOutputFlags...) }, LoginOutputFlags...)
// initCommand returns repository and *Login based on flags // initCommand returns repository and *Login based on flags
func initCommand() (*Login, string, string) { func initCommand() (*Login, string, string) {
err := loadConfig(yamlConfigPath) err := loadConfig(yamlConfigPath)
if err != nil { if err != nil {
log.Fatal("Unable to load config file " + yamlConfigPath) log.Fatal("load config file failed ", yamlConfigPath)
} }
var login *Login login, repoPath, err := curGitRepoPath(repoValue)
if loginValue == "" { if err != nil {
login, err = getActiveLogin() log.Fatal(err.Error())
if err != nil { }
log.Fatal(err)
} if loginValue != "" {
} else {
login = getLoginByName(loginValue) login = getLoginByName(loginValue)
if login == nil { if login == nil {
log.Fatal("Login name " + loginValue + " does not exist") log.Fatal("Login name " + loginValue + " does not exist")
} }
} }
repoPath := repoValue
if repoPath == "" {
login, repoPath, err = curGitRepoPath()
if err != nil {
log.Fatal(err.Error())
}
}
owner, repo := splitRepo(repoPath) owner, repo := splitRepo(repoPath)
return login, owner, repo return login, owner, repo
} }
@ -114,10 +110,16 @@ func initCommandLoginOnly() *Login {
} }
var login *Login var login *Login
if loginValue == "" {
login = getLoginByName(loginValue) login, err = getActiveLogin()
if login == nil { if err != nil {
log.Fatal("indicated login name ", loginValue, " does not exist") log.Fatal(err)
}
} else {
login = getLoginByName(loginValue)
if login == nil {
log.Fatal("Login name " + loginValue + " does not exist")
}
} }
return login return login

View File

@ -7,13 +7,11 @@ package cmd
import ( import (
"fmt" "fmt"
"log" "log"
"os"
"strconv" "strconv"
"strings"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// CmdIssues represents to login a gitea server. // CmdIssues represents to login a gitea server.
@ -21,10 +19,13 @@ var CmdIssues = cli.Command{
Name: "issues", Name: "issues",
Usage: "List and create issues", Usage: "List and create issues",
Description: `List and create issues`, Description: `List and create issues`,
ArgsUsage: "[<issue index>]",
Action: runIssues, Action: runIssues,
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
CmdIssuesList, &CmdIssuesList,
CmdIssuesCreate, &CmdIssuesCreate,
&CmdIssuesReopen,
&CmdIssuesClose,
}, },
Flags: AllDefaultFlags, Flags: AllDefaultFlags,
} }
@ -35,12 +36,18 @@ 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: AllDefaultFlags, Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "state",
Usage: "Filter by issue state (all|open|closed)",
DefaultText: "open",
},
}, AllDefaultFlags...),
} }
func runIssues(ctx *cli.Context) error { func runIssues(ctx *cli.Context) error {
if len(os.Args) == 3 { if ctx.Args().Len() == 1 {
return runIssueDetail(ctx, os.Args[2]) return runIssueDetail(ctx, ctx.Args().First())
} }
return runIssuesList(ctx) return runIssuesList(ctx)
} }
@ -48,21 +55,16 @@ func runIssues(ctx *cli.Context) error {
func runIssueDetail(ctx *cli.Context, index string) error { func runIssueDetail(ctx *cli.Context, index string) error {
login, owner, repo := initCommand() login, owner, repo := initCommand()
if strings.HasPrefix(index, "#") { idx, err := argToIndex(index)
index = index[1:]
}
idx, err := strconv.ParseInt(index, 10, 64)
if err != nil { if err != nil {
return err return err
} }
issue, err := login.Client().GetIssue(owner, repo, idx) issue, err := login.Client().GetIssue(owner, repo, idx)
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("#%d %s\n%s created %s\n\n%s", issue.Index, fmt.Printf("#%d %s\n%s created %s\n\n%s\n", issue.Index,
issue.Title, issue.Title,
issue.Poster.UserName, issue.Poster.UserName,
issue.Created.Format("2006-01-02 15:04:05"), issue.Created.Format("2006-01-02 15:04:05"),
@ -74,9 +76,19 @@ func runIssueDetail(ctx *cli.Context, index string) error {
func runIssuesList(ctx *cli.Context) error { func runIssuesList(ctx *cli.Context) error {
login, owner, repo := initCommand() login, owner, repo := initCommand()
state := gitea.StateOpen
switch ctx.String("state") {
case "all":
state = gitea.StateAll
case "open":
state = gitea.StateOpen
case "closed":
state = gitea.StateClosed
}
issues, err := login.Client().ListRepoIssues(owner, repo, gitea.ListIssueOption{ issues, err := login.Client().ListRepoIssues(owner, repo, gitea.ListIssueOption{
Page: 0, State: state,
State: string(gitea.StateOpen), Type: gitea.IssueTypeIssue,
}) })
if err != nil { if err != nil {
@ -85,7 +97,8 @@ func runIssuesList(ctx *cli.Context) error {
headers := []string{ headers := []string{
"Index", "Index",
"Name", "State",
"Author",
"Updated", "Updated",
"Title", "Title",
} }
@ -106,6 +119,7 @@ func runIssuesList(ctx *cli.Context) error {
values, values,
[]string{ []string{
strconv.FormatInt(issue.Index, 10), strconv.FormatInt(issue.Index, 10),
string(issue.State),
name, name,
issue.Updated.Format("2006-01-02 15:04:05"), issue.Updated.Format("2006-01-02 15:04:05"),
issue.Title, issue.Title,
@ -124,13 +138,15 @@ var CmdIssuesCreate = cli.Command{
Description: `Create an issue on repository`, Description: `Create an issue on repository`,
Action: runIssuesCreate, Action: runIssuesCreate,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "title, t", Name: "title",
Usage: "issue title to create", Aliases: []string{"t"},
Usage: "issue title to create",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "body, b", Name: "body",
Usage: "issue body to create", Aliases: []string{"b"},
Usage: "issue body to create",
}, },
}, LoginRepoFlags...), }, LoginRepoFlags...),
} }
@ -156,3 +172,46 @@ func runIssuesCreate(ctx *cli.Context) error {
return nil return nil
} }
// CmdIssuesReopen represents a sub command of issues to open an issue
var CmdIssuesReopen = cli.Command{
Name: "reopen",
Aliases: []string{"open"},
Usage: "Change state of an issue to 'open'",
Description: `Change state of an issue to 'open'`,
ArgsUsage: "<issue index>",
Action: func(ctx *cli.Context) error {
var s = gitea.StateOpen
return editIssueState(ctx, gitea.EditIssueOption{State: &s})
},
Flags: AllDefaultFlags,
}
// CmdIssuesClose represents a sub command of issues to close an issue
var CmdIssuesClose = cli.Command{
Name: "close",
Usage: "Change state of an issue to 'closed'",
Description: `Change state of an issue to 'closed'`,
ArgsUsage: "<issue index>",
Action: func(ctx *cli.Context) error {
var s = gitea.StateClosed
return editIssueState(ctx, gitea.EditIssueOption{State: &s})
},
Flags: AllDefaultFlags,
}
// editIssueState abstracts the arg parsing to edit the given issue
func editIssueState(ctx *cli.Context, opts gitea.EditIssueOption) error {
login, owner, repo := initCommand()
if ctx.Args().Len() == 0 {
log.Fatal(ctx.Command.ArgsUsage)
}
index, err := argToIndex(ctx.Args().First())
if err != nil {
return err
}
_, err = login.Client().EditIssue(owner, repo, index, opts)
return err
}

View File

@ -14,7 +14,7 @@ import (
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// CmdLabels represents to operate repositories' labels. // CmdLabels represents to operate repositories' labels.
@ -23,15 +23,16 @@ var CmdLabels = cli.Command{
Usage: "Manage issue labels", Usage: "Manage issue labels",
Description: `Manage issue labels`, Description: `Manage issue labels`,
Action: runLabels, Action: runLabels,
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
CmdLabelCreate, &CmdLabelCreate,
CmdLabelUpdate, &CmdLabelUpdate,
CmdLabelDelete, &CmdLabelDelete,
}, },
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "save, s", Name: "save",
Usage: "Save all the labels as a file", Aliases: []string{"s"},
Usage: "Save all the labels as a file",
}, },
}, AllDefaultFlags...), }, AllDefaultFlags...),
} }
@ -48,7 +49,7 @@ func runLabels(ctx *cli.Context) error {
var values [][]string var values [][]string
labels, err := login.Client().ListRepoLabels(owner, repo) labels, err := login.Client().ListRepoLabels(owner, repo, gitea.ListLabelsOptions{})
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -94,19 +95,19 @@ var CmdLabelCreate = cli.Command{
Description: `Create a label`, Description: `Create a label`,
Action: runLabelCreate, Action: runLabelCreate,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Usage: "label name", Usage: "label name",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "color", Name: "color",
Usage: "label color value", Usage: "label color value",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "description", Name: "description",
Usage: "label description", Usage: "label description",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "file", Name: "file",
Usage: "indicate a label file", Usage: "indicate a label file",
}, },
@ -186,19 +187,19 @@ var CmdLabelUpdate = cli.Command{
Description: `Update a label`, Description: `Update a label`,
Action: runLabelUpdate, Action: runLabelUpdate,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.IntFlag{ &cli.IntFlag{
Name: "id", Name: "id",
Usage: "label id", Usage: "label id",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Usage: "label name", Usage: "label name",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "color", Name: "color",
Usage: "label color value", Usage: "label color value",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "description", Name: "description",
Usage: "label description", Usage: "label description",
}, },
@ -246,7 +247,7 @@ var CmdLabelDelete = cli.Command{
Description: `Delete a label`, Description: `Delete a label`,
Action: runLabelCreate, Action: runLabelCreate,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.IntFlag{ &cli.IntFlag{
Name: "id", Name: "id",
Usage: "label id", Usage: "label id",
}, },

View File

@ -13,7 +13,7 @@ import (
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// CmdLogin represents to login a gitea server. // CmdLogin represents to login a gitea server.
@ -21,9 +21,10 @@ var CmdLogin = cli.Command{
Name: "login", Name: "login",
Usage: "Log in to a Gitea server", Usage: "Log in to a Gitea server",
Description: `Log in to a Gitea server`, Description: `Log in to a Gitea server`,
Subcommands: []cli.Command{ Action: runLoginList,
cmdLoginList, Subcommands: []*cli.Command{
cmdLoginAdd, &cmdLoginList,
&cmdLoginAdd,
}, },
} }
@ -33,25 +34,37 @@ var cmdLoginAdd = cli.Command{
Usage: "Add a Gitea login", Usage: "Add a Gitea login",
Description: `Add a Gitea login`, Description: `Add a Gitea login`,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "name, n", Name: "name",
Usage: "Login name", Aliases: []string{"n"},
Usage: "Login name",
Required: true,
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "url, u", Name: "url",
Value: "https://try.gitea.io", Aliases: []string{"u"},
EnvVar: "GITEA_SERVER_URL", Value: "https://try.gitea.io",
Usage: "Server URL", EnvVars: []string{"GITEA_SERVER_URL"},
Usage: "Server URL",
Required: true,
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "token, t", Name: "token",
Value: "", Aliases: []string{"t"},
EnvVar: "GITEA_SERVER_TOKEN", Value: "",
Usage: "Access token. Can be obtained from Settings > Applications", EnvVars: []string{"GITEA_SERVER_TOKEN"},
Usage: "Access token. Can be obtained from Settings > Applications",
Required: true,
}, },
cli.BoolFlag{ &cli.StringFlag{
Name: "insecure, i", Name: "ssh-key",
Usage: "Disable TLS verification", Aliases: []string{"s"},
Usage: "Path to a SSH key to use for pull/push operations",
},
&cli.BoolFlag{
Name: "insecure",
Aliases: []string{"i"},
Usage: "Disable TLS verification",
}, },
}, },
Action: runLoginAdd, Action: runLoginAdd,
@ -96,6 +109,8 @@ func runLoginAdd(ctx *cli.Context) error {
URL: ctx.String("url"), URL: ctx.String("url"),
Token: ctx.String("token"), Token: ctx.String("token"),
Insecure: ctx.Bool("insecure"), Insecure: ctx.Bool("insecure"),
SSHKey: ctx.String("ssh-key"),
User: u.UserName,
}) })
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -115,6 +130,7 @@ var cmdLoginList = cli.Command{
Usage: "List Gitea logins", Usage: "List Gitea logins",
Description: `List Gitea logins`, Description: `List Gitea logins`,
Action: runLoginList, Action: runLoginList,
Flags: []cli.Flag{&OutputFlag},
} }
func runLoginList(ctx *cli.Context) error { func runLoginList(ctx *cli.Context) error {
@ -123,10 +139,23 @@ func runLoginList(ctx *cli.Context) error {
log.Fatal("Unable to load config file " + yamlConfigPath) log.Fatal("Unable to load config file " + yamlConfigPath)
} }
fmt.Printf("Name\tURL\tSSHHost\n") headers := []string{
for _, l := range config.Logins { "Name",
fmt.Printf("%s\t%s\t%s\n", l.Name, l.URL, l.GetSSHHost()) "URL",
"SSHHost",
} }
var values [][]string
for _, l := range config.Logins {
values = append(values, []string{
l.Name,
l.URL,
l.GetSSHHost(),
})
}
Output(outputValue, headers, values)
return nil return nil
} }

View File

@ -9,7 +9,7 @@ import (
"log" "log"
"os" "os"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// CmdLogout represents to logout a gitea server. // CmdLogout represents to logout a gitea server.
@ -19,9 +19,10 @@ var CmdLogout = cli.Command{
Description: `Log out from a Gitea server`, Description: `Log out from a Gitea server`,
Action: runLogout, Action: runLogout,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "name, n", Name: "name",
Usage: "Login name to remove", Aliases: []string{"n"},
Usage: "Login name to remove",
}, },
}, },
} }

110
cmd/notifications.go Normal file
View File

@ -0,0 +1,110 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"log"
"strings"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdNotifications is the main command to operate with notifications
var CmdNotifications = cli.Command{
Name: "notifications",
Usage: "show notifications",
Description: "show notifications, by default based of the current repo and unread one",
Action: runNotifications,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "all",
Aliases: []string{"a"},
Usage: "show all notifications of related gitea instance",
},
/* // not supported jet
&cli.BoolFlag{
Name: "read",
Aliases: []string{"rd"},
Usage: "show read notifications instead unread",
},
*/
&cli.IntFlag{
Name: "page",
Aliases: []string{"p"},
Usage: "specify page, default is 1",
Value: 1,
},
&cli.IntFlag{
Name: "limit",
Aliases: []string{"lm"},
Usage: "specify limit of items per page",
},
}, AllDefaultFlags...),
}
func runNotifications(ctx *cli.Context) error {
login, owner, repo := initCommand()
client := login.Client()
var news []*gitea.NotificationThread
var err error
listOpts := gitea.ListOptions{
Page: ctx.Int("page"),
PageSize: ctx.Int("limit"),
}
if ctx.Bool("all") {
news, err = client.ListNotifications(gitea.ListNotificationOptions{
ListOptions: listOpts,
})
} else {
news, err = client.ListRepoNotifications(owner, repo, gitea.ListNotificationOptions{
ListOptions: listOpts,
})
}
if err != nil {
log.Fatal(err)
}
headers := []string{
"Type",
"Index",
"Title",
}
if ctx.Bool("all") {
headers = append(headers, "Repository")
}
var values [][]string
for _, n := range news {
if n.Subject == nil {
continue
}
// if pull or Issue get Index
var index string
if n.Subject.Type == "Issue" || n.Subject.Type == "Pull" {
index = n.Subject.URL
urlParts := strings.Split(n.Subject.URL, "/")
if len(urlParts) != 0 {
index = urlParts[len(urlParts)-1]
}
index = "#" + index
}
item := []string{n.Subject.Type, index, n.Subject.Title}
if ctx.Bool("all") {
item = append(item, n.Repository.FullName)
}
values = append(values, item)
}
if len(values) != 0 {
Output(outputValue, headers, values)
}
return nil
}

81
cmd/open.go Normal file
View File

@ -0,0 +1,81 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"log"
"path"
"strings"
local_git "code.gitea.io/tea/modules/git"
"github.com/skratchdot/open-golang/open"
"github.com/urfave/cli/v2"
)
// CmdOpen represents a sub command of issues to open issue on the web browser
var CmdOpen = cli.Command{
Name: "open",
Usage: "Open something of the repository on web browser",
Description: `Open something of the repository on web browser`,
Action: runOpen,
Flags: append([]cli.Flag{}, LoginRepoFlags...),
}
func runOpen(ctx *cli.Context) error {
login, owner, repo := initCommand()
var suffix string
number := ctx.Args().Get(0)
switch {
case strings.EqualFold(number, "issues"):
suffix = "issues"
case strings.EqualFold(number, "pulls"):
suffix = "pulls"
case strings.EqualFold(number, "releases"):
suffix = "releases"
case strings.EqualFold(number, "commits"):
repo, err := local_git.RepoForWorkdir()
if err != nil {
log.Fatal(err)
}
b, err := repo.Head()
if err != nil {
log.Fatal(err)
return nil
}
name := b.Name()
switch {
case name.IsBranch():
suffix = "commits/branch/" + name.Short()
case name.IsTag():
suffix = "commits/tag/" + name.Short()
}
case strings.EqualFold(number, "branches"):
suffix = "branches"
case strings.EqualFold(number, "wiki"):
suffix = "wiki"
case strings.EqualFold(number, "activity"):
suffix = "activity"
case strings.EqualFold(number, "settings"):
suffix = "settings"
case strings.EqualFold(number, "labels"):
suffix = "labels"
case strings.EqualFold(number, "milestones"):
suffix = "milestones"
case number != "":
suffix = "issues/" + number
default:
suffix = number
}
u := path.Join(login.URL, owner, repo, suffix)
err := open.Run(u)
if err != nil {
log.Fatal(err)
}
return nil
}

View File

@ -5,29 +5,55 @@
package cmd package cmd
import ( import (
"fmt"
"log" "log"
"strconv" "strconv"
"strings"
local_git "code.gitea.io/tea/modules/git"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/go-git/go-git/v5"
"github.com/urfave/cli" git_config "github.com/go-git/go-git/v5/config"
"github.com/urfave/cli/v2"
) )
// CmdPulls represents to login a gitea server. // CmdPulls is the main command to operate on PRs
var CmdPulls = cli.Command{ var CmdPulls = cli.Command{
Name: "pulls", Name: "pulls",
Aliases: []string{"pull", "pr"},
Usage: "List open pull requests", Usage: "List open pull requests",
Description: `List open pull requests`, Description: `List open pull requests`,
Action: runPulls, Action: runPulls,
Flags: AllDefaultFlags, Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "state",
Usage: "Filter by PR state (all|open|closed)",
DefaultText: "open",
},
}, AllDefaultFlags...),
Subcommands: []*cli.Command{
&CmdPullsCheckout,
&CmdPullsClean,
&CmdPullsCreate,
},
} }
func runPulls(ctx *cli.Context) error { func runPulls(ctx *cli.Context) error {
login, owner, repo := initCommand() login, owner, repo := initCommand()
state := gitea.StateOpen
switch ctx.String("state") {
case "all":
state = gitea.StateAll
case "open":
state = gitea.StateOpen
case "closed":
state = gitea.StateClosed
}
prs, err := login.Client().ListRepoPullRequests(owner, repo, gitea.ListPullRequestsOptions{ prs, err := login.Client().ListRepoPullRequests(owner, repo, gitea.ListPullRequestsOptions{
Page: 0, State: state,
State: string(gitea.StateOpen),
}) })
if err != nil { if err != nil {
@ -36,7 +62,8 @@ func runPulls(ctx *cli.Context) error {
headers := []string{ headers := []string{
"Index", "Index",
"Name", "State",
"Author",
"Updated", "Updated",
"Title", "Title",
} }
@ -60,6 +87,7 @@ func runPulls(ctx *cli.Context) error {
values, values,
[]string{ []string{
strconv.FormatInt(pr.Index, 10), strconv.FormatInt(pr.Index, 10),
string(pr.State),
name, name,
pr.Updated.Format("2006-01-02 15:04:05"), pr.Updated.Format("2006-01-02 15:04:05"),
pr.Title, pr.Title,
@ -70,3 +98,281 @@ func runPulls(ctx *cli.Context) error {
return nil return nil
} }
// CmdPullsCheckout is a command to locally checkout the given PR
var CmdPullsCheckout = cli.Command{
Name: "checkout",
Usage: "Locally check out the given PR",
Description: `Locally check out the given PR`,
Action: runPullsCheckout,
ArgsUsage: "<pull index>",
Flags: AllDefaultFlags,
}
func runPullsCheckout(ctx *cli.Context) error {
login, owner, repo := initCommand()
if ctx.Args().Len() != 1 {
log.Fatal("Must specify a PR index")
}
// fetch PR source-repo & -branch from gitea
idx, err := argToIndex(ctx.Args().First())
if err != nil {
return err
}
pr, err := login.Client().GetPullRequest(owner, repo, idx)
if err != nil {
return err
}
remoteURL := pr.Head.Repository.CloneURL
remoteBranchName := pr.Head.Ref
// open local git repo
localRepo, err := local_git.RepoForWorkdir()
if err != nil {
return nil
}
// verify related remote is in local repo, otherwise add it
newRemoteName := fmt.Sprintf("pulls/%v", pr.Head.Repository.Owner.UserName)
localRemote, err := localRepo.GetOrCreateRemote(remoteURL, newRemoteName)
if err != nil {
return err
}
localRemoteName := localRemote.Config().Name
localBranchName := fmt.Sprintf("pulls/%v-%v", idx, remoteBranchName)
// fetch remote
fmt.Printf("Fetching PR %v (head %s:%s) from remote '%s'\n",
idx, remoteURL, remoteBranchName, localRemoteName)
url, err := local_git.ParseURL(localRemote.Config().URLs[0])
if err != nil {
return err
}
auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey)
if err != nil {
return err
}
err = localRemote.Fetch(&git.FetchOptions{Auth: auth})
if err == git.NoErrAlreadyUpToDate {
fmt.Println(err)
} else if err != nil {
return err
}
// checkout local branch
fmt.Printf("Creating branch '%s'\n", localBranchName)
err = localRepo.TeaCreateBranch(localBranchName, remoteBranchName, localRemoteName)
if err == git.ErrBranchExists {
fmt.Println(err)
} else if err != nil {
return err
}
fmt.Printf("Checking out PR %v\n", idx)
err = localRepo.TeaCheckout(localBranchName)
return err
}
// CmdPullsClean removes the remote and local feature branches, if a PR is merged.
var CmdPullsClean = cli.Command{
Name: "clean",
Usage: "Deletes local & remote feature-branches for a closed pull request",
Description: `Deletes local & remote feature-branches for a closed pull request`,
ArgsUsage: "<pull index>",
Action: runPullsClean,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "ignore-sha",
Usage: "Find the local branch by name instead of commit hash (less precise)",
},
}, AllDefaultFlags...),
}
func runPullsClean(ctx *cli.Context) error {
login, owner, repo := initCommand()
if ctx.Args().Len() != 1 {
return fmt.Errorf("Must specify a PR index")
}
// fetch PR source-repo & -branch from gitea
idx, err := argToIndex(ctx.Args().First())
if err != nil {
return err
}
pr, err := login.Client().GetPullRequest(owner, repo, idx)
if err != nil {
return err
}
if pr.State == gitea.StateOpen {
return fmt.Errorf("PR is still open, won't delete branches")
}
// IDEA: abort if PR.Head.Repository.CloneURL does not match login.URL?
r, err := local_git.RepoForWorkdir()
if err != nil {
return err
}
// find a branch with matching sha or name, that has a remote matching the repo url
var branch *git_config.Branch
if ctx.Bool("ignore-sha") {
branch, err = r.TeaFindBranchByName(pr.Head.Ref, pr.Head.Repository.CloneURL)
} else {
branch, err = r.TeaFindBranchBySha(pr.Head.Sha, pr.Head.Repository.CloneURL)
}
if err != nil {
return err
}
if branch == nil {
if ctx.Bool("ignore-sha") {
return fmt.Errorf("Remote branch %s not found in local repo", pr.Head.Ref)
}
return fmt.Errorf(`Remote branch %s not found in local repo.
Either you don't track this PR, or the local branch has diverged from the remote.
If you still want to continue & are sure you don't loose any important commits,
call me again with the --ignore-sha flag`, pr.Head.Ref)
}
// prepare deletion of local branch:
headRef, err := r.Head()
if err != nil {
return err
}
if headRef.Name().Short() == branch.Name {
fmt.Printf("Checking out 'master' to delete local branch '%s'\n", branch.Name)
err = r.TeaCheckout("master")
if err != nil {
return err
}
}
// remove local & remote branch
fmt.Printf("Deleting local branch %s and remote branch %s\n", branch.Name, pr.Head.Ref)
url, err := r.TeaRemoteURL(branch.Remote)
if err != nil {
return err
}
auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey)
if err != nil {
return err
}
return r.TeaDeleteBranch(branch, pr.Head.Ref, auth)
}
// CmdPullsCreate creates a pull request
var CmdPullsCreate = cli.Command{
Name: "create",
Usage: "Create a pull-request",
Description: "Create a pull-request",
Action: runPullsCreate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "head",
Usage: "Set head branch (default is current one)",
},
&cli.StringFlag{
Name: "base",
Aliases: []string{"b"},
Usage: "Set base branch (default is default branch)",
},
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
Usage: "Set title of pull (default is head branch name)",
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"d"},
Usage: "Set body of new pull",
},
}, AllDefaultFlags...),
}
func runPullsCreate(ctx *cli.Context) error {
login, ownerArg, repoArg := initCommand()
client := login.Client()
repo, err := login.Client().GetRepo(ownerArg, repoArg)
if err != nil {
log.Fatal(err)
}
// open local git repo
localRepo, err := local_git.RepoForWorkdir()
if err != nil {
log.Fatal(err)
}
base := ctx.String("base")
// default is default branch
if len(base) == 0 {
base = repo.DefaultBranch
}
head := ctx.String("head")
// default is current one
if len(head) == 0 {
head, err = localRepo.TeaGetCurrentBranchName()
if err != nil {
log.Fatal(err)
}
}
title := ctx.String("title")
// default is head branch name
if len(title) == 0 {
title = head
if strings.Contains(title, ":") {
title = strings.SplitN(title, ":", 2)[1]
}
title = strings.Replace(title, "-", " ", -1)
title = strings.Replace(title, "_", " ", -1)
title = strings.Title(strings.ToLower(title))
}
// title is required
if len(title) == 0 {
fmt.Printf("Title is required")
return nil
}
// push if possible
err = localRepo.Push(&git.PushOptions{})
if err != nil {
fmt.Printf("Error occurred during 'git push':\n%s\n", err.Error())
}
pr, err := client.CreatePullRequest(ownerArg, repoArg, gitea.CreatePullRequestOption{
Head: head,
Base: base,
Title: title,
Body: ctx.String("description"),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("#%d %s\n%s created %s\n", pr.Index,
pr.Title,
pr.Poster.UserName,
pr.Created.Format("2006-01-02 15:04:05"),
)
if len(pr.Body) != 0 {
fmt.Printf("\n%s\n", pr.Body)
}
fmt.Println(pr.HTMLURL)
return nil
}
func argToIndex(arg string) (int64, error) {
if strings.HasPrefix(arg, "#") {
arg = arg[1:]
}
return strconv.ParseInt(arg, 10, 64)
}

View File

@ -11,7 +11,7 @@ import (
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// CmdReleases represents to login a gitea server. // CmdReleases represents to login a gitea server.
@ -20,8 +20,8 @@ var CmdReleases = cli.Command{
Usage: "Create releases", Usage: "Create releases",
Description: `Create releases`, Description: `Create releases`,
Action: runReleases, Action: runReleases,
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
CmdReleaseCreate, &CmdReleaseCreate,
}, },
Flags: AllDefaultFlags, Flags: AllDefaultFlags,
} }
@ -29,7 +29,7 @@ var CmdReleases = cli.Command{
func runReleases(ctx *cli.Context) error { func runReleases(ctx *cli.Context) error {
login, owner, repo := initCommand() login, owner, repo := initCommand()
releases, err := login.Client().ListReleases(owner, repo) releases, err := login.Client().ListReleases(owner, repo, gitea.ListReleasesOptions{})
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -71,33 +71,38 @@ var CmdReleaseCreate = cli.Command{
Description: `Create a release`, Description: `Create a release`,
Action: runReleaseCreate, Action: runReleaseCreate,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "tag", Name: "tag",
Usage: "Tag name", Usage: "Tag name",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "target", Name: "target",
Usage: "Target refs, branch name or commit id", Usage: "Target refs, branch name or commit id",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "title, t", Name: "title",
Usage: "Release title", Aliases: []string{"t"},
Usage: "Release title",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "note, n", Name: "note",
Usage: "Release notes", Aliases: []string{"n"},
Usage: "Release notes",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "draft, d", Name: "draft",
Usage: "Is a draft", Aliases: []string{"d"},
Usage: "Is a draft",
}, },
cli.BoolFlag{ &cli.BoolFlag{
Name: "prerelease, p", Name: "prerelease",
Usage: "Is a pre-release", Aliases: []string{"p"},
Usage: "Is a pre-release",
}, },
cli.StringSliceFlag{ &cli.StringSliceFlag{
Name: "asset, a", Name: "asset",
Usage: "List of files to attach", Aliases: []string{"a"},
Usage: "List of files to attach",
}, },
}, LoginRepoFlags...), }, LoginRepoFlags...),
} }

View File

@ -9,7 +9,7 @@ import (
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// CmdRepos represents to login a gitea server. // CmdRepos represents to login a gitea server.
@ -18,8 +18,8 @@ var CmdRepos = cli.Command{
Usage: "Operate with repositories", Usage: "Operate with repositories",
Description: `Operate with repositories`, Description: `Operate with repositories`,
Action: runReposList, Action: runReposList,
Subcommands: []cli.Command{ Subcommands: []*cli.Command{
CmdReposList, &CmdReposList,
}, },
Flags: LoginOutputFlags, Flags: LoginOutputFlags,
} }
@ -31,15 +31,15 @@ var CmdReposList = cli.Command{
Description: `List available repositories`, Description: `List available repositories`,
Action: runReposList, Action: runReposList,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
cli.StringFlag{ &cli.StringFlag{
Name: "mode", Name: "mode",
Usage: "Filter listed repositories based on mode, optional - fork, mirror, source", Usage: "Filter listed repositories based on mode, optional - fork, mirror, source",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "org", Name: "org",
Usage: "Filter listed repositories based on organization, optional", Usage: "Filter listed repositories based on organization, optional",
}, },
cli.StringFlag{ &cli.StringFlag{
Name: "user", Name: "user",
Usage: "Filter listed repositories absed on user, optional", Usage: "Filter listed repositories absed on user, optional",
}, },
@ -58,11 +58,11 @@ func runReposList(ctx *cli.Context) error {
var err error var err error
if org != "" { if org != "" {
rps, err = login.Client().ListOrgRepos(org) rps, err = login.Client().ListOrgRepos(org, gitea.ListOrgReposOptions{})
} else if user != "" { } else if user != "" {
rps, err = login.Client().ListUserRepos(user) rps, err = login.Client().ListUserRepos(user, gitea.ListReposOptions{})
} else { } else {
rps, err = login.Client().ListMyRepos() rps, err = login.Client().ListMyRepos(gitea.ListReposOptions{})
} }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

262
cmd/times.go Normal file
View File

@ -0,0 +1,262 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"fmt"
"log"
"strconv"
"strings"
"time"
"code.gitea.io/sdk/gitea"
"github.com/araddon/dateparse"
"github.com/urfave/cli/v2"
)
// CmdTrackedTimes represents the command to operate repositories' times.
var CmdTrackedTimes = cli.Command{
Name: "times",
Aliases: []string{"time"},
Usage: "Operate on tracked times of a repository's issues & pulls",
Description: `Operate on tracked times of a repository's issues & pulls.
Depending on your permissions on the repository, only your own tracked
times might be listed.`,
ArgsUsage: "[username | #issue]",
Action: runTrackedTimes,
Subcommands: []*cli.Command{
&CmdTrackedTimesAdd,
&CmdTrackedTimesDelete,
&CmdTrackedTimesReset,
},
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "from",
Aliases: []string{"f"},
Usage: "Show only times tracked after this date",
},
&cli.StringFlag{
Name: "until",
Aliases: []string{"u"},
Usage: "Show only times tracked before this date",
},
&cli.BoolFlag{
Name: "total",
Aliases: []string{"t"},
Usage: "Print the total duration at the end",
},
}, AllDefaultFlags...),
}
func runTrackedTimes(ctx *cli.Context) error {
login, owner, repo := initCommand()
client := login.Client()
if err := client.CheckServerVersionConstraint(">= 1.11"); err != nil {
return err
}
var times []*gitea.TrackedTime
var err error
user := ctx.Args().First()
fmt.Println(ctx.Command.ArgsUsage)
if user == "" {
// get all tracked times on the repo
times, err = client.GetRepoTrackedTimes(owner, repo)
} else if strings.HasPrefix(user, "#") {
// get all tracked times on the specified issue
issue, err := argToIndex(user)
if err != nil {
return err
}
times, err = client.ListTrackedTimes(owner, repo, issue, gitea.ListTrackedTimesOptions{})
} else {
// get all tracked times by the specified user
times, err = client.GetUserTrackedTimes(owner, repo, user)
}
if err != nil {
return err
}
var from, until time.Time
if ctx.String("from") != "" {
from, err = dateparse.ParseLocal(ctx.String("from"))
if err != nil {
return err
}
}
if ctx.String("until") != "" {
until, err = dateparse.ParseLocal(ctx.String("until"))
if err != nil {
return err
}
}
printTrackedTimes(times, outputValue, from, until, ctx.Bool("total"))
return nil
}
func printTrackedTimes(times []*gitea.TrackedTime, outputType string, from, until time.Time, printTotal bool) {
var outputValues [][]string
var totalDuration int64
localLoc, err := time.LoadLocation("Local") // local timezone for time formatting
if err != nil {
log.Fatal(err)
}
for _, t := range times {
if !from.IsZero() && from.After(t.Created) {
continue
}
if !until.IsZero() && until.Before(t.Created) {
continue
}
totalDuration += t.Time
outputValues = append(
outputValues,
[]string{
t.Created.In(localLoc).Format("2006-01-02 15:04:05"),
"#" + strconv.FormatInt(t.Issue.Index, 10),
t.UserName,
time.Duration(1e9 * t.Time).String(),
},
)
}
if printTotal {
outputValues = append(outputValues, []string{
"TOTAL", "", "", time.Duration(1e9 * totalDuration).String(),
})
}
headers := []string{
"Created",
"Issue",
"User",
"Duration",
}
Output(outputType, headers, outputValues)
}
// CmdTrackedTimesAdd represents a sub command of times to add time to an issue
var CmdTrackedTimesAdd = cli.Command{
Name: "add",
Usage: "Track spent time on an issue",
UsageText: "tea times add <issue> <duration>",
Description: `Track spent time on an issue
Example:
tea times add 1 1h25m
`,
Action: runTrackedTimesAdd,
Flags: LoginRepoFlags,
}
func runTrackedTimesAdd(ctx *cli.Context) error {
login, owner, repo := initCommand()
if ctx.Args().Len() < 2 {
return fmt.Errorf("No issue or duration specified.\nUsage:\t%s", ctx.Command.UsageText)
}
issue, err := argToIndex(ctx.Args().First())
if err != nil {
log.Fatal(err)
}
duration, err := time.ParseDuration(strings.Join(ctx.Args().Tail(), ""))
if err != nil {
log.Fatal(err)
}
_, err = login.Client().AddTime(owner, repo, issue, gitea.AddTimeOption{
Time: int64(duration.Seconds()),
})
if err != nil {
log.Fatal(err)
}
return nil
}
// CmdTrackedTimesDelete is a sub command of CmdTrackedTimes, and removes time from an issue
var CmdTrackedTimesDelete = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "Delete a single tracked time on an issue",
UsageText: "tea times delete <issue> <time ID>",
Action: runTrackedTimesDelete,
Flags: LoginRepoFlags,
}
func runTrackedTimesDelete(ctx *cli.Context) error {
login, owner, repo := initCommand()
client := login.Client()
if err := client.CheckServerVersionConstraint(">= 1.11"); err != nil {
return err
}
if ctx.Args().Len() < 2 {
return fmt.Errorf("No issue or time ID specified.\nUsage:\t%s", ctx.Command.UsageText)
}
issue, err := argToIndex(ctx.Args().First())
if err != nil {
log.Fatal(err)
}
timeID, err := strconv.ParseInt(ctx.Args().Get(1), 10, 64)
if err != nil {
log.Fatal(err)
}
err = client.DeleteTime(owner, repo, issue, timeID)
if err != nil {
log.Fatal(err)
}
return nil
}
// CmdTrackedTimesReset is a subcommand of CmdTrackedTimes, and
// clears all tracked times on an issue.
var CmdTrackedTimesReset = cli.Command{
Name: "reset",
Usage: "Reset tracked time on an issue",
UsageText: "tea times reset <issue>",
Action: runTrackedTimesReset,
Flags: LoginRepoFlags,
}
func runTrackedTimesReset(ctx *cli.Context) error {
login, owner, repo := initCommand()
client := login.Client()
if err := client.CheckServerVersionConstraint(">= 1.11"); err != nil {
return err
}
if ctx.Args().Len() != 1 {
return fmt.Errorf("No issue specified.\nUsage:\t%s", ctx.Command.UsageText)
}
issue, err := argToIndex(ctx.Args().First())
if err != nil {
log.Fatal(err)
}
err = client.ResetIssueTime(owner, repo, issue)
if err != nil {
log.Fatal(err)
}
return nil
}

24
go.mod
View File

@ -3,12 +3,20 @@ module code.gitea.io/tea
go 1.12 go 1.12
require ( require (
code.gitea.io/sdk/gitea v0.0.0-20191013013401-e41e9ea72caa code.gitea.io/sdk/gitea v0.12.1
github.com/go-gitea/yaml v0.0.0-20170812160011-eb3733d160e7 gitea.com/jolheiser/gitea-vet v0.1.0
github.com/mattn/go-runewidth v0.0.4 // indirect github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1
github.com/olekukonko/tablewriter v0.0.1 github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/stretchr/testify v1.3.0 github.com/go-git/go-git/v5 v5.1.0
github.com/urfave/cli v1.20.0 github.com/hashicorp/go-version v1.2.1 // indirect
gopkg.in/src-d/go-git.v4 v4.13.1 github.com/mattn/go-runewidth v0.0.9 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect github.com/olekukonko/tablewriter v0.0.4
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/stretchr/testify v1.5.1
github.com/urfave/cli/v2 v2.2.0
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed // indirect
gopkg.in/yaml.v2 v2.3.0
) )

126
go.sum
View File

@ -1,12 +1,21 @@
code.gitea.io/sdk/gitea v0.0.0-20191013013401-e41e9ea72caa h1:KgpwNF1StxPXMfCD9M++jvCUPUqHPAbuvQn1q3sWtqw= code.gitea.io/sdk/gitea v0.12.1 h1:bMgjEqPnNX/i6TpVwXwpjJtFOnUSuC9P6yy/jjy8sjY=
code.gitea.io/sdk/gitea v0.0.0-20191013013401-e41e9ea72caa/go.mod h1:8IxkM1gyiwEjfO0m47bcmr3u3foR15+LoVub43hCHd0= code.gitea.io/sdk/gitea v0.12.1/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
gitea.com/jolheiser/gitea-vet v0.1.0 h1:gJEms9YWbIcrPOEmDOJ+5JZXCYFxNpwxlI73uRulAi4=
gitea.com/jolheiser/gitea-vet v0.1.0/go.mod h1:2Oa6TAdEp1N/38oBNh3ZeiSEER60D/CeDaBFv2sdH58=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
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-20200409225146-d820a6159ab1 h1:TEBmxO80TM04L8IuMWk77SGL1HomBmKTdzdJLLWznxI=
github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
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/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
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.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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=
@ -16,10 +25,22 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= 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-gitea/yaml v0.0.0-20170812160011-eb3733d160e7 h1:/FEVbfrJ50yBk73Lyq1oCZ4VaCc0g1xd9xLHjz+Znf8= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-gitea/yaml v0.0.0-20170812160011-eb3733d160e7/go.mod h1:WjJPyqjAk/UMv+Fk/ZRjEOh5SXszSALnSzKqICd7pNg= 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.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc=
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk=
github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM=
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/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/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
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.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@ -28,61 +49,96 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
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/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.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
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/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
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/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
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/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
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/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
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.3.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-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-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
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-20200625203802-6e8e738ad208/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-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-20190221075227-b4e8571b14e0/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-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
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-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= 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/tools v0.0.0-20200713011307-fd294ab11aed h1:+qzWo37K31KxduIYaBeMqJ8MUOyTayOQKpH9aDPLMSY=
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
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=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

27
main.go
View File

@ -13,11 +13,11 @@ import (
"code.gitea.io/tea/cmd" "code.gitea.io/tea/cmd"
"code.gitea.io/tea/modules/setting" "code.gitea.io/tea/modules/setting"
"github.com/urfave/cli" "github.com/urfave/cli/v2"
) )
// Version holds the current Gitea version // Version holds the current tea version
var Version = "0.1.0-dev" var Version = "development"
// Tags holds the build tags used // Tags holds the build tags used
var Tags = "" var Tags = ""
@ -29,18 +29,21 @@ func init() {
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "Tea" app.Name = "tea"
app.Usage = "Command line tool to interact with Gitea" app.Usage = "Command line tool to interact with Gitea"
app.Description = `` app.Description = ``
app.Version = Version + formatBuiltWith(Tags) app.Version = Version + formatBuiltWith(Tags)
app.Commands = []cli.Command{ app.Commands = []*cli.Command{
cmd.CmdLogin, &cmd.CmdLogin,
cmd.CmdLogout, &cmd.CmdLogout,
cmd.CmdIssues, &cmd.CmdIssues,
cmd.CmdPulls, &cmd.CmdPulls,
cmd.CmdReleases, &cmd.CmdReleases,
cmd.CmdRepos, &cmd.CmdRepos,
cmd.CmdLabels, &cmd.CmdLabels,
&cmd.CmdTrackedTimes,
&cmd.CmdOpen,
&cmd.CmdNotifications,
} }
app.EnableBashCompletion = true app.EnableBashCompletion = true
err := app.Run(os.Args) err := app.Run(os.Args)

120
modules/git/auth.go Normal file
View File

@ -0,0 +1,120 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package git
import (
"bufio"
"fmt"
"io/ioutil"
"net/url"
"os"
"os/user"
"path/filepath"
"strings"
git_transport "github.com/go-git/go-git/v5/plumbing/transport"
gogit_http "github.com/go-git/go-git/v5/plumbing/transport/http"
gogit_ssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
)
// GetAuthForURL returns the appropriate AuthMethod to be used in Push() / Pull()
// operations depending on the protocol, and prompts the user for credentials if
// necessary.
func GetAuthForURL(remoteURL *url.URL, httpUser, keyFile string) (auth git_transport.AuthMethod, err error) {
user := remoteURL.User.Username()
switch remoteURL.Scheme {
case "https":
if httpUser != "" {
user = httpUser
}
if user == "" {
user, err = promptUser(remoteURL.Host)
if err != nil {
return nil, err
}
}
pass, isSet := remoteURL.User.Password()
if !isSet {
pass, err = promptPass(remoteURL.Host)
if err != nil {
return nil, err
}
}
auth = &gogit_http.BasicAuth{Password: pass, Username: user}
case "ssh":
// try to select right key via ssh-agent. if it fails, try to read a key manually
auth, err = gogit_ssh.DefaultAuthBuilder(user)
if err != nil {
signer, err := readSSHPrivKey(keyFile)
if err != nil {
return nil, err
}
auth = &gogit_ssh.PublicKeys{User: user, Signer: signer}
}
default:
return nil, fmt.Errorf("don't know how to handle url scheme %v", remoteURL.Scheme)
}
return auth, nil
}
func readSSHPrivKey(keyFile string) (sig ssh.Signer, err error) {
if keyFile != "" {
keyFile, err = absPathWithExpansion(keyFile)
} else {
keyFile, err = absPathWithExpansion("~/.ssh/id_rsa")
}
if err != nil {
return nil, err
}
sshKey, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, err
}
sig, err = ssh.ParsePrivateKey(sshKey)
if err != nil {
pass, err := promptPass(keyFile)
if err != nil {
return nil, err
}
sig, err = ssh.ParsePrivateKeyWithPassphrase(sshKey, []byte(pass))
if err != nil {
return nil, err
}
}
return sig, err
}
func promptUser(domain string) (string, error) {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("%s username: ", domain)
username, err := reader.ReadString('\n')
return strings.TrimSpace(username), err
}
func promptPass(domain string) (string, error) {
fmt.Printf("%s password: ", domain)
pass, err := terminal.ReadPassword(0)
return string(pass), err
}
func absPathWithExpansion(p string) (string, error) {
u, err := user.Current()
if err != nil {
return "", err
}
if p == "~" {
return u.HomeDir, nil
} else if strings.HasPrefix(p, "~/") {
return filepath.Join(u.HomeDir, p[2:]), nil
} else {
return filepath.Abs(p)
}
}

191
modules/git/branch.go Normal file
View File

@ -0,0 +1,191 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package git
import (
"fmt"
"strings"
"github.com/go-git/go-git/v5"
git_config "github.com/go-git/go-git/v5/config"
git_plumbing "github.com/go-git/go-git/v5/plumbing"
git_transport "github.com/go-git/go-git/v5/plumbing/transport"
)
// TeaCreateBranch creates a new branch in the repo, tracking from another branch.
func (r TeaRepo) TeaCreateBranch(localBranchName, remoteBranchName, remoteName string) error {
// save in .git/config to assign remote for future pulls
localBranchRefName := git_plumbing.NewBranchReferenceName(localBranchName)
err := r.CreateBranch(&git_config.Branch{
Name: localBranchName,
Merge: git_plumbing.NewBranchReferenceName(remoteBranchName),
Remote: remoteName,
})
if err != nil {
return err
}
// serialize the branch to .git/refs/heads
remoteBranchRefName := git_plumbing.NewRemoteReferenceName(remoteName, remoteBranchName)
remoteBranchRef, err := r.Storer.Reference(remoteBranchRefName)
if err != nil {
return err
}
localHashRef := git_plumbing.NewHashReference(localBranchRefName, remoteBranchRef.Hash())
return r.Storer.SetReference(localHashRef)
}
// TeaCheckout checks out the given branch in the worktree.
func (r TeaRepo) TeaCheckout(branchName string) error {
tree, err := r.Worktree()
if err != nil {
return err
}
localBranchRefName := git_plumbing.NewBranchReferenceName(branchName)
return tree.Checkout(&git.CheckoutOptions{Branch: localBranchRefName})
}
// TeaDeleteBranch removes the given branch locally, and if `remoteBranch` is
// not empty deletes it at it's remote repo.
func (r TeaRepo) TeaDeleteBranch(branch *git_config.Branch, remoteBranch string, auth git_transport.AuthMethod) error {
err := r.DeleteBranch(branch.Name)
// if the branch is not found that's ok, as .git/config may have no entry if
// no remote tracking branch is configured for it (eg push without -u flag)
if err != nil && err.Error() != "branch not found" {
return err
}
err = r.Storer.RemoveReference(git_plumbing.NewBranchReferenceName(branch.Name))
if err != nil {
return err
}
if remoteBranch != "" {
// delete remote branch via git protocol:
// an empty source in the refspec means remote deletion to git 🙃
refspec := fmt.Sprintf(":%s", git_plumbing.NewBranchReferenceName(remoteBranch))
err = r.Push(&git.PushOptions{
RemoteName: branch.Remote,
RefSpecs: []git_config.RefSpec{git_config.RefSpec(refspec)},
Prune: true,
Auth: auth,
})
}
return err
}
// TeaFindBranchBySha returns a branch that is at the the given SHA and syncs to the
// given remote repo.
func (r TeaRepo) TeaFindBranchBySha(sha, repoURL string) (b *git_config.Branch, err error) {
// find remote matching our repoURL
remote, err := r.GetRemote(repoURL)
if err != nil {
return nil, err
}
if remote == nil {
return nil, fmt.Errorf("No remote found for '%s'", repoURL)
}
remoteName := remote.Config().Name
// check if the given remote has our branch (.git/refs/remotes/<remoteName>/*)
iter, err := r.References()
if err != nil {
return nil, err
}
defer iter.Close()
var remoteRefName git_plumbing.ReferenceName
var localRefName git_plumbing.ReferenceName
err = iter.ForEach(func(ref *git_plumbing.Reference) error {
if ref.Name().IsRemote() {
name := ref.Name().Short()
if ref.Hash().String() == sha && strings.HasPrefix(name, remoteName) {
remoteRefName = ref.Name()
}
}
if ref.Name().IsBranch() && ref.Hash().String() == sha {
localRefName = ref.Name()
}
return nil
})
if err != nil {
return nil, err
}
if remoteRefName == "" || localRefName == "" {
// no remote tracking branch found, so a potential local branch
// can't be a match either
return nil, nil
}
b = &git_config.Branch{
Remote: remoteName,
Name: localRefName.Short(),
Merge: localRefName,
}
return b, b.Validate()
}
// TeaFindBranchByName returns a branch that is at the the given local and
// remote names and syncs to the given remote repo. This method is less precise
// than TeaFindBranchBySha(), but may be desirable if local and remote branch
// have diverged.
func (r TeaRepo) TeaFindBranchByName(branchName, repoURL string) (b *git_config.Branch, err error) {
// find remote matching our repoURL
remote, err := r.GetRemote(repoURL)
if err != nil {
return nil, err
}
if remote == nil {
return nil, fmt.Errorf("No remote found for '%s'", repoURL)
}
remoteName := remote.Config().Name
// check if the given remote has our branch (.git/refs/remotes/<remoteName>/*)
iter, err := r.References()
if err != nil {
return nil, err
}
defer iter.Close()
var remoteRefName git_plumbing.ReferenceName
var localRefName git_plumbing.ReferenceName
var remoteSearchingName = fmt.Sprintf("%s/%s", remoteName, branchName)
err = iter.ForEach(func(ref *git_plumbing.Reference) error {
if ref.Name().IsRemote() && ref.Name().Short() == remoteSearchingName {
remoteRefName = ref.Name()
}
n := ref.Name()
if n.IsBranch() && n.Short() == branchName {
localRefName = n
}
return nil
})
if err != nil {
return nil, err
}
if remoteRefName == "" || localRefName == "" {
return nil, nil
}
b = &git_config.Branch{
Remote: remoteName,
Name: localRefName.Short(),
Merge: localRefName,
}
return b, b.Validate()
}
// TeaGetCurrentBranchName return the name of the branch witch is currently active
func (r TeaRepo) TeaGetCurrentBranchName() (string, error) {
localHead, err := r.Head()
if err != nil {
return "", err
}
if !localHead.Name().IsBranch() {
return "", fmt.Errorf("active ref is no branch")
}
return strings.TrimLeft(localHead.Name().String(), "refs/heads/"), nil
}

76
modules/git/remote.go Normal file
View File

@ -0,0 +1,76 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package git
import (
"fmt"
"net/url"
"github.com/go-git/go-git/v5"
git_config "github.com/go-git/go-git/v5/config"
)
// GetRemote tries to match a Remote of the repo via the given URL.
// Matching is based on the normalized URL, accepting different protocols.
func (r TeaRepo) GetRemote(remoteURL string) (*git.Remote, error) {
repoURL, err := ParseURL(remoteURL)
if err != nil {
return nil, err
}
remotes, err := r.Remotes()
if err != nil {
return nil, err
}
for _, r := range remotes {
for _, u := range r.Config().URLs {
remoteURL, err := ParseURL(u)
if err != nil {
return nil, err
}
if remoteURL.Host == repoURL.Host && remoteURL.Path == repoURL.Path {
return r, nil
}
}
}
return nil, nil
}
// GetOrCreateRemote tries to match a Remote of the repo via the given URL.
// If no match is found, a new Remote with `newRemoteName` is created.
// Matching is based on the normalized URL, accepting different protocols.
func (r TeaRepo) GetOrCreateRemote(remoteURL, newRemoteName string) (*git.Remote, error) {
localRemote, err := r.GetRemote(remoteURL)
if err != nil {
return nil, err
}
// if no match found, create a new remote
if localRemote == nil {
localRemote, err = r.CreateRemote(&git_config.RemoteConfig{
Name: newRemoteName,
URLs: []string{remoteURL},
})
if err != nil {
return nil, err
}
}
return localRemote, nil
}
// TeaRemoteURL returns the first url entry for the given remote name
func (r TeaRepo) TeaRemoteURL(name string) (auth *url.URL, err error) {
remote, err := r.Remote(name)
if err != nil {
return nil, err
}
urls := remote.Config().URLs
if len(urls) == 0 {
return nil, fmt.Errorf("remote %s has no URL configured", name)
}
return ParseURL(remote.Config().URLs[0])
}

39
modules/git/repo.go Normal file
View File

@ -0,0 +1,39 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package git
import (
"github.com/go-git/go-git/v5"
)
// TeaRepo is a go-git Repository, with an extended high level interface.
type TeaRepo struct {
*git.Repository
}
// RepoForWorkdir tries to open the git repository in the local directory
// for reading or modification.
func RepoForWorkdir() (*TeaRepo, error) {
repo, err := git.PlainOpenWithOptions("./", &git.PlainOpenOptions{
DetectDotGit: true,
})
if err != nil {
return nil, err
}
return &TeaRepo{repo}, nil
}
// RepoFromPath tries to open the git repository by path
func RepoFromPath(path string) (*TeaRepo, error) {
repo, err := git.PlainOpenWithOptions(path, &git.PlainOpenOptions{
DetectDotGit: true,
})
if err != nil {
return nil, err
}
return &TeaRepo{repo}, nil
}

View File

@ -1,3 +1,7 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package git package git
import ( import (
@ -36,6 +40,11 @@ func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) {
u.Path = strings.TrimPrefix(u.Path, "/") u.Path = strings.TrimPrefix(u.Path, "/")
} }
// .git suffix is optional and breaks normalization
if strings.HasSuffix(u.Path, ".git") {
u.Path = strings.TrimSuffix(u.Path, ".git")
}
return return
} }

View File

@ -11,10 +11,16 @@ import (
"fmt" "fmt"
) )
// AdminListOrgsOptions options for listing admin's organizations
type AdminListOrgsOptions struct {
ListOptions
}
// AdminListOrgs lists all orgs // AdminListOrgs lists all orgs
func (c *Client) AdminListOrgs() ([]*Organization, error) { func (c *Client) AdminListOrgs(opt AdminListOrgsOptions) ([]*Organization, error) {
orgs := make([]*Organization, 0, 10) opt.setDefaults()
return orgs, c.getParsedResponse("GET", "/admin/orgs", nil, nil, &orgs) orgs := make([]*Organization, 0, opt.PageSize)
return orgs, c.getParsedResponse("GET", fmt.Sprintf("/admin/orgs?%s", opt.getURLQuery().Encode()), nil, nil, &orgs)
} }
// AdminCreateOrg create an organization // AdminCreateOrg create an organization

View File

@ -11,10 +11,16 @@ import (
"fmt" "fmt"
) )
// AdminListUsersOptions options for listing admin users
type AdminListUsersOptions struct {
ListOptions
}
// AdminListUsers lists all users // AdminListUsers lists all users
func (c *Client) AdminListUsers() ([]*User, error) { func (c *Client) AdminListUsers(opt AdminListUsersOptions) ([]*User, error) {
users := make([]*User, 0, 10) opt.setDefaults()
return users, c.getParsedResponse("GET", "/admin/users", nil, nil, &users) users := make([]*User, 0, opt.PageSize)
return users, c.getParsedResponse("GET", fmt.Sprintf("/admin/users?%s", opt.getURLQuery().Encode()), nil, nil, &users)
} }
// CreateUserOption create user options // CreateUserOption create user options

View File

@ -24,11 +24,17 @@ type Attachment struct {
DownloadURL string `json:"browser_download_url"` DownloadURL string `json:"browser_download_url"`
} }
// ListReleaseAttachmentsOptions options for listing release's attachments
type ListReleaseAttachmentsOptions struct {
ListOptions
}
// ListReleaseAttachments list release's attachments // ListReleaseAttachments list release's attachments
func (c *Client) ListReleaseAttachments(user, repo string, release int64) ([]*Attachment, error) { func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt ListReleaseAttachmentsOptions) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10) opt.setDefaults()
attachments := make([]*Attachment, 0, opt.PageSize)
err := c.getParsedResponse("GET", err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release), fmt.Sprintf("/repos/%s/%s/releases/%d/assets?%s", user, repo, release, opt.getURLQuery().Encode()),
nil, nil, &attachments) nil, nil, &attachments)
return attachments, err return attachments, err
} }

View File

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea 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.
@ -12,21 +13,29 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
"sync"
"github.com/hashicorp/go-version"
) )
var jsonHeader = http.Header{"content-type": []string{"application/json"}} var jsonHeader = http.Header{"content-type": []string{"application/json"}}
// Version return the library version // Version return the library version
func Version() string { func Version() string {
return "0.12.3" return "0.12.0"
} }
// Client represents a Gogs API client. // Client represents a Gitea API client.
type Client struct { type Client struct {
url string url string
accessToken string accessToken string
sudo string username string
client *http.Client password string
otp string
sudo string
client *http.Client
serverVersion *version.Version
versionLock sync.RWMutex
} }
// NewClient initializes and returns a API client. // NewClient initializes and returns a API client.
@ -39,9 +48,20 @@ func NewClient(url, token string) *Client {
} }
// NewClientWithHTTP creates an API client with a custom http client // NewClientWithHTTP creates an API client with a custom http client
func NewClientWithHTTP(url string, httpClient *http.Client) { func NewClientWithHTTP(url string, httpClient *http.Client) *Client {
client := NewClient(url, "") client := NewClient(url, "")
client.client = httpClient client.client = httpClient
return client
}
// SetBasicAuth sets basicauth
func (c *Client) SetBasicAuth(username, password string) {
c.username, c.password = username, password
}
// SetOTP sets OTP for 2FA
func (c *Client) SetOTP(otp string) {
c.otp = otp
} }
// SetHTTPClient replaces default http.Client with user given one. // SetHTTPClient replaces default http.Client with user given one.
@ -62,7 +82,13 @@ func (c *Client) doRequest(method, path string, header http.Header, body io.Read
if len(c.accessToken) != 0 { if len(c.accessToken) != 0 {
req.Header.Set("Authorization", "token "+c.accessToken) req.Header.Set("Authorization", "token "+c.accessToken)
} }
if c.sudo != "" { if len(c.otp) != 0 {
req.Header.Set("X-GITEA-OTP", c.otp)
}
if len(c.username) != 0 {
req.SetBasicAuth(c.username, c.password)
}
if len(c.sudo) != 0 {
req.Header.Set("Sudo", c.sudo) req.Header.Set("Sudo", c.sudo)
} }
for k, v := range header { for k, v := range header {
@ -100,7 +126,7 @@ func (c *Client) getResponse(method, path string, header http.Header, body io.Re
if err = json.Unmarshal(data, &errMap); err != nil { if err = json.Unmarshal(data, &errMap); err != nil {
// when the JSON can't be parsed, data was probably empty or a plain string, // when the JSON can't be parsed, data was probably empty or a plain string,
// so we try to return a helpful error anyway // so we try to return a helpful error anyway
return nil, fmt.Errorf("Unknown API Error: %d %s", resp.StatusCode, string(data)) return nil, fmt.Errorf("Unknown API Error: %d\nRequest: '%s' with '%s' method '%s' header and '%s' body", resp.StatusCode, path, method, header, string(data))
} }
return nil, errors.New(errMap["message"].(string)) return nil, errors.New(errMap["message"].(string))
} }

View File

@ -10,13 +10,18 @@ import (
"fmt" "fmt"
) )
// ListForksOptions options for listing repository's forks
type ListForksOptions struct {
ListOptions
}
// ListForks list a repository's forks // ListForks list a repository's forks
func (c *Client) ListForks(user, repo string) ([]*Repository, error) { func (c *Client) ListForks(user string, repo string, opt ListForksOptions) ([]*Repository, error) {
forks := make([]*Repository, 10) opt.setDefaults()
err := c.getParsedResponse("GET", forks := make([]*Repository, opt.PageSize)
fmt.Sprintf("/repos/%s/%s/forks", user, repo), return forks, c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/forks?%s", user, repo, opt.getURLQuery().Encode()),
nil, nil, &forks) nil, nil, &forks)
return forks, err
} }
// CreateForkOption options for creating a fork // CreateForkOption options for creating a fork
@ -32,8 +37,5 @@ func (c *Client) CreateFork(user, repo string, form CreateForkOption) (*Reposito
return nil, err return nil, err
} }
fork := new(Repository) fork := new(Repository)
err = c.getParsedResponse("POST", return fork, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/forks", user, repo), jsonHeader, bytes.NewReader(body), &fork)
fmt.Sprintf("/repos/%s/%s/forks", user, repo),
jsonHeader, bytes.NewReader(body), &fork)
return fork, err
} }

24
vendor/code.gitea.io/sdk/gitea/git_blob.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"fmt"
)
// GitBlobResponse represents a git blob
type GitBlobResponse struct {
Content string `json:"content"`
Encoding string `json:"encoding"`
URL string `json:"url"`
SHA string `json:"sha"`
Size int64 `json:"size"`
}
// GetBlob get the blob of a repository file
func (c *Client) GetBlob(user, repo, sha string) (*GitBlobResponse, error) {
blob := new(GitBlobResponse)
return blob, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/blobs/%s", user, repo, sha), nil, nil, blob)
}

View File

@ -17,10 +17,16 @@ type GitHook struct {
Content string `json:"content,omitempty"` Content string `json:"content,omitempty"`
} }
// ListRepoGitHooksOptions options for listing repository's githooks
type ListRepoGitHooksOptions struct {
ListOptions
}
// 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) ([]*GitHook, error) { func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions) ([]*GitHook, error) {
hooks := make([]*GitHook, 0, 10) opt.setDefaults()
return hooks, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git", user, repo), nil, nil, &hooks) hooks := make([]*GitHook, 0, opt.PageSize)
return hooks, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
} }
// GetRepoGitHook get a Git hook of a repository // GetRepoGitHook get a Git hook of a repository

View File

@ -1,3 +1,8 @@
module code.gitea.io/sdk/gitea module code.gitea.io/sdk/gitea
go 1.12 go 1.12
require (
github.com/hashicorp/go-version v1.2.0
github.com/stretchr/testify v1.4.0
)

View File

@ -0,0 +1,13 @@
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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -24,16 +24,23 @@ type Hook struct {
Created time.Time `json:"created_at"` Created time.Time `json:"created_at"`
} }
// ListHooksOptions options for listing hooks
type ListHooksOptions struct {
ListOptions
}
// ListOrgHooks list all the hooks of one organization // ListOrgHooks list all the hooks of one organization
func (c *Client) ListOrgHooks(org string) ([]*Hook, error) { func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, error) {
hooks := make([]*Hook, 0, 10) opt.setDefaults()
return hooks, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks", org), nil, nil, &hooks) hooks := make([]*Hook, 0, opt.PageSize)
return hooks, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks?%s", org, opt.getURLQuery().Encode()), nil, nil, &hooks)
} }
// ListRepoHooks list all the hooks of one repository // ListRepoHooks list all the hooks of one repository
func (c *Client) ListRepoHooks(user, repo string) ([]*Hook, error) { func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook, error) {
hooks := make([]*Hook, 0, 10) opt.setDefaults()
return hooks, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks", user, repo), nil, nil, &hooks) hooks := make([]*Hook, 0, opt.PageSize)
return hooks, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
} }
// GetOrgHook get a hook of an organization // GetOrgHook get a hook of an organization

View File

@ -9,6 +9,8 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"strings"
"time" "time"
) )
@ -18,6 +20,14 @@ type PullRequestMeta struct {
Merged *time.Time `json:"merged_at"` Merged *time.Time `json:"merged_at"`
} }
// RepositoryMeta basic repository information
type RepositoryMeta struct {
ID int64 `json:"id"`
Name string `json:"name"`
Owner string `json:"owner"`
FullName string `json:"full_name"`
}
// Issue represents an issue in a repository // Issue represents an issue in a repository
type Issue struct { type Issue struct {
ID int64 `json:"id"` ID int64 `json:"id"`
@ -40,36 +50,112 @@ type Issue struct {
Closed *time.Time `json:"closed_at"` Closed *time.Time `json:"closed_at"`
Deadline *time.Time `json:"due_date"` Deadline *time.Time `json:"due_date"`
PullRequest *PullRequestMeta `json:"pull_request"` PullRequest *PullRequestMeta `json:"pull_request"`
Repository *RepositoryMeta `json:"repository"`
} }
// ListIssueOption list issue options // ListIssueOption list issue options
type ListIssueOption struct { type ListIssueOption struct {
Page int ListOptions
State string State StateType
Type IssueType
Labels []string
Milestones []string
KeyWord string
}
// StateType issue state type
type StateType string
const (
// StateOpen pr/issue is opend
StateOpen StateType = "open"
// StateClosed pr/issue is closed
StateClosed StateType = "closed"
// StateAll is all
StateAll StateType = "all"
)
// IssueType is issue a pull or only an issue
type IssueType string
const (
// IssueTypeAll pr and issue
IssueTypeAll IssueType = ""
// IssueTypeIssue only issues
IssueTypeIssue IssueType = "issues"
// IssueTypePull only pulls
IssueTypePull IssueType = "pulls"
)
// QueryEncode turns options into querystring argument
func (opt *ListIssueOption) QueryEncode() string {
query := opt.getURLQuery()
if len(opt.State) > 0 {
query.Add("state", string(opt.State))
}
if len(opt.Labels) > 0 {
query.Add("labels", strings.Join(opt.Labels, ","))
}
if len(opt.KeyWord) > 0 {
query.Add("q", opt.KeyWord)
}
query.Add("type", string(opt.Type))
if len(opt.Milestones) > 0 {
query.Add("milestones", strings.Join(opt.Milestones, ","))
}
return query.Encode()
} }
// ListIssues returns all issues assigned the authenticated user // ListIssues returns all issues assigned the authenticated user
func (c *Client) ListIssues(opt ListIssueOption) ([]*Issue, error) { func (c *Client) ListIssues(opt ListIssueOption) ([]*Issue, error) {
issues := make([]*Issue, 0, 10) opt.setDefaults()
return issues, c.getParsedResponse("GET", fmt.Sprintf("/issues?page=%d", opt.Page), nil, nil, &issues) issues := make([]*Issue, 0, opt.PageSize)
}
// ListUserIssues returns all issues assigned to the authenticated user link, _ := url.Parse("/repos/issues/search")
func (c *Client) ListUserIssues(opt ListIssueOption) ([]*Issue, error) { link.RawQuery = opt.QueryEncode()
issues := make([]*Issue, 0, 10) err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &issues)
return issues, c.getParsedResponse("GET", fmt.Sprintf("/user/issues?page=%d", opt.Page), nil, nil, &issues) if e := c.CheckServerVersionConstraint(">=1.12.0"); e != nil {
for i := 0; i < len(issues); i++ {
if issues[i].Repository != nil {
issues[i].Repository.Owner = strings.Split(issues[i].Repository.FullName, "/")[0]
}
}
}
return issues, err
} }
// 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, error) { func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Issue, error) {
issues := make([]*Issue, 0, 10) opt.setDefaults()
return issues, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues?page=%d", owner, repo, opt.Page), nil, nil, &issues) issues := make([]*Issue, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues", owner, repo))
link.RawQuery = opt.QueryEncode()
err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &issues)
if e := c.CheckServerVersionConstraint(">=1.12.0"); e != nil {
for i := 0; i < len(issues); i++ {
if issues[i].Repository != nil {
issues[i].Repository.Owner = strings.Split(issues[i].Repository.FullName, "/")[0]
}
}
}
return issues, err
} }
// 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, error) { func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, error) {
issue := new(Issue) issue := new(Issue)
return issue, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue) err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue)
if e := c.CheckServerVersionConstraint(">=1.12.0"); e != nil && issue.Repository != nil {
issue.Repository.Owner = strings.Split(issue.Repository.FullName, "/")[0]
}
return issue, err
} }
// CreateIssueOption options to create one issue // CreateIssueOption options to create one issue
@ -105,7 +191,7 @@ type EditIssueOption struct {
Assignee *string `json:"assignee"` Assignee *string `json:"assignee"`
Assignees []string `json:"assignees"` Assignees []string `json:"assignees"`
Milestone *int64 `json:"milestone"` Milestone *int64 `json:"milestone"`
State *string `json:"state"` State *StateType `json:"state"`
Deadline *time.Time `json:"due_date"` Deadline *time.Time `json:"due_date"`
} }
@ -119,19 +205,3 @@ func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption)
return issue, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), return issue, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index),
jsonHeader, bytes.NewReader(body), issue) jsonHeader, bytes.NewReader(body), issue)
} }
// StartIssueStopWatch starts a stopwatch for an existing issue for a given
// repository
func (c *Client) StartIssueStopWatch(owner, repo string, index int64) error {
_, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index),
jsonHeader, nil)
return err
}
// StopIssueStopWatch stops an existing stopwatch for an issue in a given
// repository
func (c *Client) StopIssueStopWatch(owner, repo string, index int64) error {
_, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index),
jsonHeader, nil)
return err
}

View File

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"time" "time"
) )
@ -25,16 +26,50 @@ type Comment struct {
Updated time.Time `json:"updated_at"` Updated time.Time `json:"updated_at"`
} }
// ListIssueCommentOptions list comment options
type ListIssueCommentOptions struct {
ListOptions
Since time.Time
Before time.Time
}
// QueryEncode turns options into querystring argument
func (opt *ListIssueCommentOptions) QueryEncode() string {
query := opt.getURLQuery()
if !opt.Since.IsZero() {
query.Add("since", opt.Since.Format(time.RFC3339))
}
if !opt.Before.IsZero() {
query.Add("before", opt.Before.Format(time.RFC3339))
}
return query.Encode()
}
// ListIssueComments list comments on an issue. // ListIssueComments list comments on an issue.
func (c *Client) ListIssueComments(owner, repo string, index int64) ([]*Comment, error) { func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssueCommentOptions) ([]*Comment, error) {
comments := make([]*Comment, 0, 10) opt.setDefaults()
return comments, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index), nil, nil, &comments) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index))
link.RawQuery = opt.QueryEncode()
comments := make([]*Comment, 0, opt.PageSize)
return comments, c.getParsedResponse("GET", link.String(), nil, nil, &comments)
} }
// ListRepoIssueComments list comments for a given repo. // ListRepoIssueComments list comments for a given repo.
func (c *Client) ListRepoIssueComments(owner, repo string) ([]*Comment, error) { func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentOptions) ([]*Comment, error) {
comments := make([]*Comment, 0, 10) opt.setDefaults()
return comments, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo), nil, nil, &comments) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo))
link.RawQuery = opt.QueryEncode()
comments := make([]*Comment, 0, opt.PageSize)
return comments, c.getParsedResponse("GET", link.String(), nil, nil, &comments)
}
// GetIssueComment get a comment for a given repo by id.
func (c *Client) GetIssueComment(owner, repo string, id int64) (*Comment, error) {
comment := new(Comment)
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return comment, err
}
return comment, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, id), nil, nil, &comment)
} }
// CreateIssueCommentOption options for creating a comment on an issue // CreateIssueCommentOption options for creating a comment on an issue

View File

@ -20,10 +20,16 @@ type Label struct {
URL string `json:"url"` URL string `json:"url"`
} }
// ListLabelsOptions options for listing repository's labels
type ListLabelsOptions struct {
ListOptions
}
// ListRepoLabels list labels of one repository // ListRepoLabels list labels of one repository
func (c *Client) ListRepoLabels(owner, repo string) ([]*Label, error) { func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*Label, error) {
labels := make([]*Label, 0, 10) opt.setDefaults()
return labels, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels", owner, repo), nil, nil, &labels) labels := make([]*Label, 0, opt.PageSize)
return labels, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels?%s", owner, repo, opt.getURLQuery().Encode()), nil, nil, &labels)
} }
// GetRepoLabel get one label of repository by repo it // GetRepoLabel get one label of repository by repo it
@ -43,6 +49,11 @@ type CreateLabelOption struct {
// CreateLabel create one label of repository // CreateLabel create one label of repository
func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, error) { func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, error) {
if len(opt.Color) == 6 {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
opt.Color = "#" + opt.Color
}
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@ -77,9 +88,9 @@ func (c *Client) DeleteLabel(owner, repo string, id int64) error {
} }
// 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) ([]*Label, error) { func (c *Client) GetIssueLabels(owner, repo string, index int64, opts ListLabelsOptions) ([]*Label, error) {
labels := make([]*Label, 0, 5) labels := make([]*Label, 0, 5)
return labels, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil, &labels) return labels, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels?%s", owner, repo, index, opts.getURLQuery().Encode()), nil, nil, &labels)
} }
// IssueLabelsOption a collection of labels // IssueLabelsOption a collection of labels

View File

@ -8,21 +8,10 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"time" "time"
) )
// StateType issue state type
type StateType string
const (
// StateOpen pr is opend
StateOpen StateType = "open"
// StateClosed pr is closed
StateClosed StateType = "closed"
// StateAll is all
StateAll StateType = "all"
)
// Milestone milestone is a collection of issues on one repository // Milestone milestone is a collection of issues on one repository
type Milestone struct { type Milestone struct {
ID int64 `json:"id"` ID int64 `json:"id"`
@ -35,10 +24,30 @@ type Milestone struct {
Deadline *time.Time `json:"due_on"` Deadline *time.Time `json:"due_on"`
} }
// ListMilestoneOption list milestone options
type ListMilestoneOption struct {
ListOptions
// open, closed, all
State StateType
}
// QueryEncode turns options into querystring argument
func (opt *ListMilestoneOption) QueryEncode() string {
query := opt.getURLQuery()
if opt.State != "" {
query.Add("state", string(opt.State))
}
return query.Encode()
}
// ListRepoMilestones list all the milestones of one repository // ListRepoMilestones list all the milestones of one repository
func (c *Client) ListRepoMilestones(owner, repo string) ([]*Milestone, error) { func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption) ([]*Milestone, error) {
milestones := make([]*Milestone, 0, 10) opt.setDefaults()
return milestones, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones", owner, repo), nil, nil, &milestones) milestones := make([]*Milestone, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/milestones", owner, repo))
link.RawQuery = opt.QueryEncode()
return milestones, c.getParsedResponse("GET", link.String(), nil, nil, &milestones)
} }
// GetMilestone get one milestone by repo name and milestone id // GetMilestone get one milestone by repo name and milestone id
@ -51,6 +60,7 @@ func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, error)
type CreateMilestoneOption struct { type CreateMilestoneOption struct {
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
State StateType `json:"state"`
Deadline *time.Time `json:"due_on"` Deadline *time.Time `json:"due_on"`
} }
@ -61,7 +71,18 @@ func (c *Client) CreateMilestone(owner, repo string, opt CreateMilestoneOption)
return nil, err return nil, err
} }
milestone := new(Milestone) milestone := new(Milestone)
return milestone, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/milestones", owner, repo), jsonHeader, bytes.NewReader(body), milestone) err = c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/milestones", owner, repo), jsonHeader, bytes.NewReader(body), milestone)
// make creating closed milestones need gitea >= v1.13.0
// this make it backwards compatible
if err == nil && opt.State == StateClosed && milestone.State != StateClosed {
closed := "closed"
return c.EditMilestone(owner, repo, milestone.ID, EditMilestoneOption{
State: &closed,
})
}
return milestone, err
} }
// EditMilestoneOption options for editing a milestone // EditMilestoneOption options for editing a milestone

98
vendor/code.gitea.io/sdk/gitea/issue_reaction.go generated vendored Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"bytes"
"encoding/json"
"fmt"
"time"
)
// Reaction contain one reaction
type Reaction struct {
User *User `json:"user"`
Reaction string `json:"content"`
Created time.Time `json:"created_at"`
}
// GetIssueReactions get a list reactions of an issue
func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction, error) {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return nil, err
}
reactions := make([]*Reaction, 0, 10)
return reactions, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index), nil, nil, &reactions)
}
// GetIssueCommentReactions get a list of reactions from a comment of an issue
func (c *Client) GetIssueCommentReactions(owner, repo string, commentID int64) ([]*Reaction, error) {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return nil, err
}
reactions := make([]*Reaction, 0, 10)
return reactions, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID), nil, nil, &reactions)
}
// editReactionOption contain the reaction type
type editReactionOption struct {
Reaction string `json:"content"`
}
// PostIssueReaction add a reaction to an issue
func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction string) (*Reaction, error) {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return nil, err
}
reactionResponse := new(Reaction)
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
if err != nil {
return nil, err
}
return reactionResponse, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index),
jsonHeader, bytes.NewReader(body), reactionResponse)
}
// DeleteIssueReaction remove a reaction from an issue
func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction string) error {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return err
}
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
if err != nil {
return err
}
_, err = c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/reactions", owner, repo, index), jsonHeader, bytes.NewReader(body))
return err
}
// PostIssueCommentReaction add a reaction to a comment of an issue
func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Reaction, error) {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return nil, err
}
reactionResponse := new(Reaction)
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
if err != nil {
return nil, err
}
return reactionResponse, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID),
jsonHeader, bytes.NewReader(body), reactionResponse)
}
// DeleteIssueCommentReaction remove a reaction from a comment of an issue
func (c *Client) DeleteIssueCommentReaction(owner, repo string, commentID int64, reaction string) error {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return err
}
// swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueDeleteCommentReaction
body, err := json.Marshal(&editReactionOption{Reaction: reaction})
if err != nil {
return err
}
_, err = c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/comments/%d/reactions", owner, repo, commentID),
jsonHeader, bytes.NewReader(body))
return err
}

42
vendor/code.gitea.io/sdk/gitea/issue_stopwatch.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"fmt"
"time"
)
// StopWatch represents a running stopwatch of an issue / pr
type StopWatch struct {
Created time.Time `json:"created"`
IssueIndex int64 `json:"issue_index"`
}
// GetMyStopwatches list all stopwatches
func (c *Client) GetMyStopwatches() ([]*StopWatch, error) {
stopwatches := make([]*StopWatch, 0, 1)
return stopwatches, c.getParsedResponse("GET", "/user/stopwatches", nil, nil, &stopwatches)
}
// DeleteIssueStopwatch delete / cancel a specific stopwatch
func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) error {
_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/delete", owner, repo, index), nil, nil)
return err
}
// StartIssueStopWatch starts a stopwatch for an existing issue for a given
// repository
func (c *Client) StartIssueStopWatch(owner, repo string, index int64) error {
_, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index), nil, nil)
return err
}
// StopIssueStopWatch stops an existing stopwatch for an issue in a given
// repository
func (c *Client) StopIssueStopWatch(owner, repo string, index int64) error {
_, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index), nil, nil)
return err
}

82
vendor/code.gitea.io/sdk/gitea/issue_subscription.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"fmt"
"net/http"
)
// GetIssueSubscribers get list of users who subscribed on an issue
func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User, error) {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return nil, err
}
subscribers := make([]*User, 0, 10)
return subscribers, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions", owner, repo, index), nil, nil, &subscribers)
}
// AddIssueSubscription Subscribe user to issue
func (c *Client) AddIssueSubscription(owner, repo string, index int64, user string) error {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return err
}
status, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
if err != nil {
return err
}
if status == http.StatusCreated {
return nil
}
if status == http.StatusOK {
return fmt.Errorf("already subscribed")
}
return fmt.Errorf("unexpected Status: %d", status)
}
// DeleteIssueSubscription unsubscribe user from issue
func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user string) error {
if err := c.CheckServerVersionConstraint(">=1.11.0"); err != nil {
return err
}
status, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
if err != nil {
return err
}
if status == http.StatusCreated {
return nil
}
if status == http.StatusOK {
return fmt.Errorf("already unsubscribed")
}
return fmt.Errorf("unexpected Status: %d", status)
}
// CheckIssueSubscription check if current user is subscribed to an issue
func (c *Client) CheckIssueSubscription(owner, repo string, index int64) (*WatchInfo, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
wi := new(WatchInfo)
return wi, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/check", owner, repo, index), nil, nil, wi)
}
// IssueSubscribe subscribe current user to an issue
func (c *Client) IssueSubscribe(owner, repo string, index int64) error {
u, err := c.GetMyUserInfo()
if err != nil {
return err
}
return c.AddIssueSubscription(owner, repo, index, u.UserName)
}
// IssueUnSubscribe unsubscribe current user from an issue
func (c *Client) IssueUnSubscribe(owner, repo string, index int64) error {
u, err := c.GetMyUserInfo()
if err != nil {
return err
}
return c.DeleteIssueSubscription(owner, repo, index, u.UserName)
}

View File

@ -16,9 +16,13 @@ type TrackedTime struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
// Time in seconds // Time in seconds
Time int64 `json:"time"` Time int64 `json:"time"`
UserID int64 `json:"user_id"` // deprecated (only for backwards compatibility)
IssueID int64 `json:"issue_id"` UserID int64 `json:"user_id"`
UserName string `json:"user_name"`
// deprecated (only for backwards compatibility)
IssueID int64 `json:"issue_id"`
Issue *Issue `json:"issue"`
} }
// GetUserTrackedTimes list tracked times of a user // GetUserTrackedTimes list tracked times of a user
@ -42,7 +46,11 @@ func (c *Client) GetMyTrackedTimes() ([]*TrackedTime, error) {
// AddTimeOption options for adding time to an issue // AddTimeOption options for adding time to an issue
type AddTimeOption struct { type AddTimeOption struct {
// time in seconds // time in seconds
Time int64 `json:"time"` Time int64 `json:"time" binding:"Required"`
// optional
Created time.Time `json:"created"`
// optional
User string `json:"user_name"`
} }
// AddTime adds time to issue with the given index // AddTime adds time to issue with the given index
@ -56,8 +64,26 @@ func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*T
jsonHeader, bytes.NewReader(body), t) jsonHeader, bytes.NewReader(body), t)
} }
// ListTrackedTimes get tracked times of one issue via issue id // ListTrackedTimesOptions options for listing repository's tracked times
func (c *Client) ListTrackedTimes(owner, repo string, index int64) ([]*TrackedTime, error) { type ListTrackedTimesOptions struct {
times := make([]*TrackedTime, 0, 5) ListOptions
return times, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), nil, nil, &times) }
// ListTrackedTimes list tracked times of a single issue for a given repository
func (c *Client) ListTrackedTimes(owner, repo string, index int64, opt ListTrackedTimesOptions) ([]*TrackedTime, error) {
opt.setDefaults()
times := make([]*TrackedTime, 0, opt.PageSize)
return times, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/times?%s", owner, repo, index, opt.getURLQuery().Encode()), nil, nil, &times)
}
// ResetIssueTime reset tracked time of a single issue for a given repository
func (c *Client) ResetIssueTime(owner, repo string, index int64) error {
_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), nil, nil)
return err
}
// 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) error {
_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times/%d", owner, repo, index, timeID), nil, nil)
return err
} }

37
vendor/code.gitea.io/sdk/gitea/list_options.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"fmt"
"net/url"
)
const defaultPageSize = 10
const maxPageSize = 50
// ListOptions options for using Gitea's API pagination
type ListOptions struct {
Page int
PageSize int
}
func (o ListOptions) getURLQuery() url.Values {
query := make(url.Values)
query.Add("page", fmt.Sprintf("%d", o.Page))
query.Add("limit", fmt.Sprintf("%d", o.PageSize))
return query
}
func (o ListOptions) setDefaults() {
if o.Page < 1 {
o.Page = 1
}
if o.PageSize < 0 || o.PageSize > maxPageSize {
o.PageSize = defaultPageSize
}
}

View File

@ -1,13 +0,0 @@
// Copyright 2015 The Gogs 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
// ServerVersion returns the version of the server
func (c *Client) ServerVersion() (string, error) {
var v = struct {
Version string `json:"version"`
}{}
return v.Version, c.getParsedResponse("GET", "/api/v1/version", nil, nil, &v)
}

137
vendor/code.gitea.io/sdk/gitea/notifications.go generated vendored Normal file
View File

@ -0,0 +1,137 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"fmt"
"net/url"
"time"
)
// NotificationThread expose Notification on API
type NotificationThread struct {
ID int64 `json:"id"`
Repository *Repository `json:"repository"`
Subject *NotificationSubject `json:"subject"`
Unread bool `json:"unread"`
Pinned bool `json:"pinned"`
UpdatedAt time.Time `json:"updated_at"`
URL string `json:"url"`
}
// NotificationSubject contains the notification subject (Issue/Pull/Commit)
type NotificationSubject struct {
Title string `json:"title"`
URL string `json:"url"`
LatestCommentURL string `json:"latest_comment_url"`
Type string `json:"type" binding:"In(Issue,Pull,Commit)"`
}
// ListNotificationOptions represents the filter options
type ListNotificationOptions struct {
ListOptions
Since time.Time
Before time.Time
}
// MarkNotificationOptions represents the filter options
type MarkNotificationOptions struct {
LastReadAt time.Time
}
// QueryEncode encode options to url query
func (opt *ListNotificationOptions) QueryEncode() string {
query := opt.getURLQuery()
if !opt.Since.IsZero() {
query.Add("since", opt.Since.Format(time.RFC3339))
}
if !opt.Before.IsZero() {
query.Add("before", opt.Before.Format(time.RFC3339))
}
return query.Encode()
}
// QueryEncode encode options to url query
func (opt *MarkNotificationOptions) QueryEncode() string {
query := make(url.Values)
if !opt.LastReadAt.IsZero() {
query.Add("last_read_at", opt.LastReadAt.Format(time.RFC3339))
}
return query.Encode()
}
// CheckNotifications list users's notification threads
func (c *Client) CheckNotifications() (int64, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return 0, err
}
new := struct {
New int64 `json:"new"`
}{}
return new.New, c.getParsedResponse("GET", "/notifications/new", jsonHeader, nil, &new)
}
// GetNotification get notification thread by ID
func (c *Client) GetNotification(id int64) (*NotificationThread, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
thread := new(NotificationThread)
return thread, c.getParsedResponse("GET", fmt.Sprintf("/notifications/threads/%d", id), nil, nil, thread)
}
// ReadNotification mark notification thread as read by ID
func (c *Client) ReadNotification(id int64) error {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return err
}
_, err := c.getResponse("PATCH", fmt.Sprintf("/notifications/threads/%d", id), nil, nil)
return err
}
// ListNotifications list users's notification threads
func (c *Client) ListNotifications(opt ListNotificationOptions) ([]*NotificationThread, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
link, _ := url.Parse("/notifications")
link.RawQuery = opt.QueryEncode()
threads := make([]*NotificationThread, 0, 10)
return threads, c.getParsedResponse("GET", link.String(), nil, nil, &threads)
}
// ReadNotifications mark notification threads as read
func (c *Client) ReadNotifications(opt MarkNotificationOptions) error {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return err
}
link, _ := url.Parse("/notifications")
link.RawQuery = opt.QueryEncode()
_, err := c.getResponse("PUT", link.String(), nil, nil)
return err
}
// ListRepoNotifications list users's notification threads on a specific repo
func (c *Client) ListRepoNotifications(owner, reponame string, opt ListNotificationOptions) ([]*NotificationThread, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, reponame))
link.RawQuery = opt.QueryEncode()
threads := make([]*NotificationThread, 0, 10)
return threads, c.getParsedResponse("GET", link.String(), nil, nil, &threads)
}
// ReadRepoNotifications mark notification threads as read on a specific repo
func (c *Client) ReadRepoNotifications(owner, reponame string, opt MarkNotificationOptions) error {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, reponame))
link.RawQuery = opt.QueryEncode()
_, err := c.getResponse("PUT", link.String(), nil, nil)
return err
}

87
vendor/code.gitea.io/sdk/gitea/oauth2.go generated vendored Normal file
View File

@ -0,0 +1,87 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"bytes"
"encoding/json"
"fmt"
"time"
)
// Oauth2 represents an Oauth2 Application
type Oauth2 struct {
ID int64 `json:"id"`
Name string `json:"name"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RedirectURIs []string `json:"redirect_uris"`
Created time.Time `json:"created"`
}
// ListOauth2Option for listing Oauth2 Applications
type ListOauth2Option struct {
ListOptions
}
// CreateOauth2Option required options for creating an Application
type CreateOauth2Option struct {
Name string `json:"name"`
RedirectURIs []string `json:"redirect_uris"`
}
// CreateOauth2 create an Oauth2 Application and returns a completed Oauth2 object.
func (c *Client) CreateOauth2(opt CreateOauth2Option) (*Oauth2, error) {
if e := c.CheckServerVersionConstraint(">=1.12.0"); e != nil {
return nil, e
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
oauth := new(Oauth2)
return oauth, c.getParsedResponse("POST", "/user/applications/oauth2", jsonHeader, bytes.NewReader(body), oauth)
}
// UpdateOauth2 a specific Oauth2 Application by ID and return a completed Oauth2 object.
func (c *Client) UpdateOauth2(oauth2id int64, opt CreateOauth2Option) (*Oauth2, error) {
if e := c.CheckServerVersionConstraint(">=1.12.0"); e != nil {
return nil, e
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
oauth := new(Oauth2)
return oauth, c.getParsedResponse("PATCH", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), jsonHeader, bytes.NewReader(body), oauth)
}
// GetOauth2 a specific Oauth2 Application by ID.
func (c *Client) GetOauth2(oauth2id int64) (*Oauth2, error) {
if e := c.CheckServerVersionConstraint(">=1.12.0"); e != nil {
return nil, e
}
oauth2s := &Oauth2{}
return oauth2s, c.getParsedResponse("GET", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), nil, nil, &oauth2s)
}
// ListOauth2 all of your Oauth2 Applications.
func (c *Client) ListOauth2(opt ListOauth2Option) ([]*Oauth2, error) {
if e := c.CheckServerVersionConstraint(">=1.12.0"); e != nil {
return nil, e
}
opt.setDefaults()
oauth2s := make([]*Oauth2, 0, opt.PageSize)
return oauth2s, c.getParsedResponse("GET", fmt.Sprintf("/user/applications/oauth2?%s", opt.getURLQuery().Encode()), nil, nil, &oauth2s)
}
// DeleteOauth2 delete an Oauth2 application by ID
func (c *Client) DeleteOauth2(oauth2id int64) error {
if e := c.CheckServerVersionConstraint(">=1.12.0"); e != nil {
return e
}
_, err := c.getResponse("DELETE", fmt.Sprintf("/user/applications/oauth2/%d", oauth2id), nil, nil)
return err
}

View File

@ -23,16 +23,23 @@ type Organization struct {
Visibility string `json:"visibility"` Visibility string `json:"visibility"`
} }
// ListOrgsOptions options for listing organizations
type ListOrgsOptions struct {
ListOptions
}
// ListMyOrgs list all of current user's organizations // ListMyOrgs list all of current user's organizations
func (c *Client) ListMyOrgs() ([]*Organization, error) { func (c *Client) ListMyOrgs(opt ListOrgsOptions) ([]*Organization, error) {
orgs := make([]*Organization, 0, 5) opt.setDefaults()
return orgs, c.getParsedResponse("GET", "/user/orgs", nil, nil, &orgs) orgs := make([]*Organization, 0, opt.PageSize)
return orgs, c.getParsedResponse("GET", fmt.Sprintf("/user/orgs?%s", opt.getURLQuery().Encode()), nil, nil, &orgs)
} }
// ListUserOrgs list all of some user's organizations // ListUserOrgs list all of some user's organizations
func (c *Client) ListUserOrgs(user string) ([]*Organization, error) { func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization, error) {
orgs := make([]*Organization, 0, 5) opt.setDefaults()
return orgs, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs", user), nil, nil, &orgs) orgs := make([]*Organization, 0, opt.PageSize)
return orgs, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs?%s", user, opt.getURLQuery().Encode()), nil, nil, &orgs)
} }
// GetOrg get one organization by name // GetOrg get one organization by name

View File

@ -1,26 +1,98 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2020 The Gitea 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.
package gitea package gitea
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
"net/http"
"net/url"
) )
// AddOrgMembershipOption add user to organization options // DeleteOrgMembership remove a member from an organization
type AddOrgMembershipOption struct { func (c *Client) DeleteOrgMembership(org, user string) error {
Role string `json:"role"` _, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil)
return err
} }
// AddOrgMembership add some one to an organization's member // ListOrgMembershipOption list OrgMembership options
func (c *Client) AddOrgMembership(org, user string, opt AddOrgMembershipOption) error { type ListOrgMembershipOption struct {
body, err := json.Marshal(&opt) ListOptions
}
// ListOrgMembership list an organization's members
func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, error) {
opt.setDefaults()
users := make([]*User, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/members", url.PathEscape(org)))
link.RawQuery = opt.getURLQuery().Encode()
return users, c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
}
// ListPublicOrgMembership list an organization's members
func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, error) {
opt.setDefaults()
users := make([]*User, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/public_members", url.PathEscape(org)))
link.RawQuery = opt.getURLQuery().Encode()
return users, c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
}
// CheckOrgMembership Check if a user is a member of an organization
func (c *Client) CheckOrgMembership(org, user string) (bool, error) {
status, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil)
if err != nil {
return false, err
}
switch status {
case http.StatusNoContent:
return true, nil
case http.StatusNotFound:
return false, nil
default:
return false, fmt.Errorf("unexpected Status: %d", status)
}
}
// CheckPublicOrgMembership Check if a user is a member of an organization
func (c *Client) CheckPublicOrgMembership(org, user string) (bool, error) {
status, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil)
if err != nil {
return false, err
}
switch status {
case http.StatusNoContent:
return true, nil
case http.StatusNotFound:
return false, nil
default:
return false, fmt.Errorf("unexpected Status: %d", status)
}
}
// SetPublicOrgMembership publicize/conceal a user's membership
func (c *Client) SetPublicOrgMembership(org, user string, visible bool) error {
var (
status int
err error
)
if visible {
status, err = c.getStatusCode("PUT", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil)
} else {
status, err = c.getStatusCode("DELETE", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil)
}
if err != nil { if err != nil {
return err return err
} }
_, err = c.getResponse("PUT", fmt.Sprintf("/orgs/%s/membership/%s", org, user), jsonHeader, bytes.NewReader(body)) switch status {
return err case http.StatusNoContent:
return nil
case http.StatusNotFound:
return fmt.Errorf("forbidden")
default:
return fmt.Errorf("unexpected Status: %d", status)
}
} }

View File

@ -22,16 +22,23 @@ type Team struct {
Units []string `json:"units"` Units []string `json:"units"`
} }
// ListTeamsOptions options for listing teams
type ListTeamsOptions struct {
ListOptions
}
// ListOrgTeams lists all teams of an organization // ListOrgTeams lists all teams of an organization
func (c *Client) ListOrgTeams(org string) ([]*Team, error) { func (c *Client) ListOrgTeams(org string, opt ListTeamsOptions) ([]*Team, error) {
teams := make([]*Team, 0, 10) opt.setDefaults()
return teams, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams", org), nil, nil, &teams) teams := make([]*Team, 0, opt.PageSize)
return teams, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams?%s", org, opt.getURLQuery().Encode()), nil, nil, &teams)
} }
// ListMyTeams lists all the teams of the current user // ListMyTeams lists all the teams of the current user
func (c *Client) ListMyTeams() ([]*Team, error) { func (c *Client) ListMyTeams(opt *ListTeamsOptions) ([]*Team, error) {
teams := make([]*Team, 0, 10) opt.setDefaults()
return teams, c.getParsedResponse("GET", "/user/teams", nil, nil, &teams) teams := make([]*Team, 0, opt.PageSize)
return teams, c.getParsedResponse("GET", fmt.Sprintf("/user/teams?%s", opt.getURLQuery().Encode()), nil, nil, &teams)
} }
// GetTeam gets a team by ID // GetTeam gets a team by ID
@ -86,10 +93,16 @@ func (c *Client) DeleteTeam(id int64) error {
return err return err
} }
// ListTeamMembersOptions options for listing team's members
type ListTeamMembersOptions struct {
ListOptions
}
// ListTeamMembers lists all members of a team // ListTeamMembers lists all members of a team
func (c *Client) ListTeamMembers(id int64) ([]*User, error) { func (c *Client) ListTeamMembers(id int64, opt ListTeamMembersOptions) ([]*User, error) {
members := make([]*User, 0, 10) opt.setDefaults()
return members, c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members", id), nil, nil, &members) members := make([]*User, 0, opt.PageSize)
return members, c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members?%s", id, opt.getURLQuery().Encode()), nil, nil, &members)
} }
// GetTeamMember gets a member of a team // GetTeamMember gets a member of a team
@ -110,10 +123,16 @@ func (c *Client) RemoveTeamMember(id int64, user string) error {
return err return err
} }
// ListTeamRepositoriesOptions options for listing team's repositories
type ListTeamRepositoriesOptions struct {
ListOptions
}
// ListTeamRepositories lists all repositories of a team // ListTeamRepositories lists all repositories of a team
func (c *Client) ListTeamRepositories(id int64) ([]*Repository, error) { func (c *Client) ListTeamRepositories(id int64, opt ListTeamRepositoriesOptions) ([]*Repository, error) {
repos := make([]*Repository, 0, 10) opt.setDefaults()
return repos, c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/repos", id), nil, nil, &repos) repos := make([]*Repository, 0, opt.PageSize)
return repos, c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/repos?%s", id, opt.getURLQuery().Encode()), nil, nil, &repos)
} }
// AddTeamRepository adds a repository to a team // AddTeamRepository adds a repository to a team

View File

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea 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.
@ -8,6 +9,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"time" "time"
) )
@ -57,18 +59,50 @@ type PullRequest struct {
// ListPullRequestsOptions options for listing pull requests // ListPullRequestsOptions options for listing pull requests
type ListPullRequestsOptions struct { type ListPullRequestsOptions struct {
Page int `json:"page"` ListOptions
State string `json:"state"` State StateType `json:"state"`
// oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority
Sort string
Milestone int64
}
// MergeStyle is used specify how a pull is merged
type MergeStyle string
const (
// MergeStyleMerge merge pull as usual
MergeStyleMerge MergeStyle = "merge"
// MergeStyleRebase rebase pull
MergeStyleRebase MergeStyle = "rebase"
// MergeStyleRebaseMerge rebase and merge pull
MergeStyleRebaseMerge MergeStyle = "rebase-merge"
// MergeStyleSquash squash and merge pull
MergeStyleSquash MergeStyle = "squash"
)
// QueryEncode turns options into querystring argument
func (opt *ListPullRequestsOptions) QueryEncode() string {
query := opt.getURLQuery()
if len(opt.State) > 0 {
query.Add("state", string(opt.State))
}
if len(opt.Sort) > 0 {
query.Add("sort", opt.Sort)
}
if opt.Milestone > 0 {
query.Add("milestone", fmt.Sprintf("%d", opt.Milestone))
}
return query.Encode()
} }
// ListRepoPullRequests list PRs of one repository // ListRepoPullRequests list PRs of one repository
func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, error) { func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, error) {
body, err := json.Marshal(&opt) opt.setDefaults()
if err != nil { prs := make([]*PullRequest, 0, opt.PageSize)
return nil, err
} link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", owner, repo))
prs := make([]*PullRequest, 0, 10) link.RawQuery = opt.QueryEncode()
return prs, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls", owner, repo), jsonHeader, bytes.NewReader(body), &prs) return prs, c.getParsedResponse("GET", link.String(), jsonHeader, nil, &prs)
} }
// GetPullRequest get information of one PR // GetPullRequest get information of one PR
@ -105,11 +139,12 @@ func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOpti
type EditPullRequestOption struct { type EditPullRequestOption struct {
Title string `json:"title"` Title string `json:"title"`
Body string `json:"body"` Body string `json:"body"`
Base string `json:"base"`
Assignee string `json:"assignee"` Assignee string `json:"assignee"`
Assignees []string `json:"assignees"` Assignees []string `json:"assignees"`
Milestone int64 `json:"milestone"` Milestone int64 `json:"milestone"`
Labels []int64 `json:"labels"` Labels []int64 `json:"labels"`
State *string `json:"state"` State *StateType `json:"state"`
Deadline *time.Time `json:"due_date"` Deadline *time.Time `json:"due_date"`
} }
@ -124,10 +159,29 @@ func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRe
jsonHeader, bytes.NewReader(body), pr) jsonHeader, bytes.NewReader(body), pr)
} }
// MergePullRequestOption options when merging a pull request
type MergePullRequestOption struct {
Style MergeStyle `json:"Do"`
Title string `json:"MergeTitleField"`
Message string `json:"MergeMessageField"`
}
// 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) error { func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePullRequestOption) (bool, error) {
_, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil) if opt.Style == MergeStyleSquash {
return err if err := c.CheckServerVersionConstraint(">=1.11.5"); err != nil {
return false, err
}
}
body, err := json.Marshal(&opt)
if err != nil {
return false, err
}
status, err := c.getStatusCode("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), jsonHeader, bytes.NewReader(body))
if err != nil {
return false, err
}
return status == 200, nil
} }
// IsPullRequestMerged test if one PR is merged to one repository // IsPullRequestMerged test if one PR is merged to one repository

184
vendor/code.gitea.io/sdk/gitea/pull_review.go generated vendored Normal file
View File

@ -0,0 +1,184 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"bytes"
"encoding/json"
"fmt"
"net/url"
"time"
)
// ReviewStateType review state type
type ReviewStateType string
const (
// ReviewStateApproved pr is approved
ReviewStateApproved ReviewStateType = "APPROVED"
// ReviewStatePending pr state is pending
ReviewStatePending ReviewStateType = "PENDING"
// ReviewStateComment is a comment review
ReviewStateComment ReviewStateType = "COMMENT"
// ReviewStateRequestChanges changes for pr are requested
ReviewStateRequestChanges ReviewStateType = "REQUEST_CHANGES"
// ReviewStateRequestReview review is requested from user
ReviewStateRequestReview ReviewStateType = "REQUEST_REVIEW"
// ReviewStateUnknown state of pr is unknown
ReviewStateUnknown ReviewStateType = ""
)
// PullReview represents a pull request review
type PullReview struct {
ID int64 `json:"id"`
Reviewer *User `json:"user"`
State ReviewStateType `json:"state"`
Body string `json:"body"`
CommitID string `json:"commit_id"`
Stale bool `json:"stale"`
Official bool `json:"official"`
CodeCommentsCount int `json:"comments_count"`
// swagger:strfmt date-time
Submitted time.Time `json:"submitted_at"`
HTMLURL string `json:"html_url"`
HTMLPullURL string `json:"pull_request_url"`
}
// PullReviewComment represents a comment on a pull request review
type PullReviewComment struct {
ID int64 `json:"id"`
Body string `json:"body"`
Reviewer *User `json:"user"`
ReviewID int64 `json:"pull_request_review_id"`
// swagger:strfmt date-time
Created time.Time `json:"created_at"`
// swagger:strfmt date-time
Updated time.Time `json:"updated_at"`
Path string `json:"path"`
CommitID string `json:"commit_id"`
OrigCommitID string `json:"original_commit_id"`
DiffHunk string `json:"diff_hunk"`
LineNum uint64 `json:"position"`
OldLineNum uint64 `json:"original_position"`
HTMLURL string `json:"html_url"`
HTMLPullURL string `json:"pull_request_url"`
}
// CreatePullReviewOptions are options to create a pull review
type CreatePullReviewOptions struct {
State ReviewStateType `json:"event"`
Body string `json:"body"`
CommitID string `json:"commit_id"`
Comments []CreatePullReviewComment `json:"comments"`
}
// CreatePullReviewComment represent a review comment for creation api
type CreatePullReviewComment struct {
// the tree path
Path string `json:"path"`
Body string `json:"body"`
// if comment to old file line or 0
OldLineNum int64 `json:"old_position"`
// if comment to new file line or 0
NewLineNum int64 `json:"new_position"`
}
// SubmitPullReviewOptions are options to submit a pending pull review
type SubmitPullReviewOptions struct {
State ReviewStateType `json:"event"`
Body string `json:"body"`
}
// ListPullReviewsOptions options for listing PullReviews
type ListPullReviewsOptions struct {
ListOptions
}
// ListPullReviews lists all reviews of a pull request
func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullReviewsOptions) ([]*PullReview, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
opt.setDefaults()
rs := make([]*PullReview, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews", owner, repo, index))
link.RawQuery = opt.ListOptions.getURLQuery().Encode()
return rs, c.getParsedResponse("GET", link.String(), jsonHeader, nil, &rs)
}
// GetPullReview gets a specific review of a pull request
func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
r := new(PullReview)
return r, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), jsonHeader, nil, &r)
}
// ListPullReviewsCommentsOptions options for listing PullReviewsComments
type ListPullReviewsCommentsOptions struct {
ListOptions
}
// ListPullReviewComments lists all comments of a pull request review
func (c *Client) ListPullReviewComments(owner, repo string, index, id int64, opt ListPullReviewsCommentsOptions) ([]*PullReviewComment, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
opt.setDefaults()
rcl := make([]*PullReviewComment, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/comments", owner, repo, index, id))
link.RawQuery = opt.ListOptions.getURLQuery().Encode()
return rcl, c.getParsedResponse("GET", link.String(), jsonHeader, nil, &rcl)
}
// DeletePullReview delete a specific review from a pull request
func (c *Client) DeletePullReview(owner, repo string, index, id int64) error {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return err
}
_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id), jsonHeader, nil)
return err
}
// CreatePullReview create a review to an pull request
func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePullReviewOptions) (*PullReview, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
r := new(PullReview)
return r, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews", owner, repo, index),
jsonHeader, bytes.NewReader(body), r)
}
// SubmitPullReview submit a pending review to an pull request
func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt SubmitPullReviewOptions) (*PullReview, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
r := new(PullReview)
return r, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d", owner, repo, index, id),
jsonHeader, bytes.NewReader(body), r)
}

View File

@ -29,11 +29,17 @@ type Release struct {
Attachments []*Attachment `json:"assets"` Attachments []*Attachment `json:"assets"`
} }
// ListReleasesOptions options for listing repository's releases
type ListReleasesOptions struct {
ListOptions
}
// ListReleases list releases of a repository // ListReleases list releases of a repository
func (c *Client) ListReleases(user, repo string) ([]*Release, error) { func (c *Client) ListReleases(user, repo string, opt ListReleasesOptions) ([]*Release, error) {
releases := make([]*Release, 0, 10) opt.setDefaults()
releases := make([]*Release, 0, opt.PageSize)
err := c.getParsedResponse("GET", err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases", user, repo), fmt.Sprintf("/repos/%s/%s/releases?%s", user, repo, opt.getURLQuery().Encode()),
nil, nil, &releases) nil, nil, &releases)
return releases, err return releases, err
} }

View File

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"time" "time"
) )
@ -56,22 +57,105 @@ type Repository struct {
AvatarURL string `json:"avatar_url"` AvatarURL string `json:"avatar_url"`
} }
// ListReposOptions options for listing repositories
type ListReposOptions struct {
ListOptions
}
// ListMyRepos lists all repositories for the authenticated user that has access to. // ListMyRepos lists all repositories for the authenticated user that has access to.
func (c *Client) ListMyRepos() ([]*Repository, error) { func (c *Client) ListMyRepos(opt ListReposOptions) ([]*Repository, error) {
repos := make([]*Repository, 0, 10) opt.setDefaults()
return repos, c.getParsedResponse("GET", "/user/repos", nil, nil, &repos) repos := make([]*Repository, 0, opt.PageSize)
return repos, c.getParsedResponse("GET", fmt.Sprintf("/user/repos?%s", opt.getURLQuery().Encode()), nil, nil, &repos)
} }
// 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) ([]*Repository, error) { func (c *Client) ListUserRepos(user string, opt ListReposOptions) ([]*Repository, error) {
repos := make([]*Repository, 0, 10) opt.setDefaults()
return repos, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos", user), nil, nil, &repos) repos := make([]*Repository, 0, opt.PageSize)
return repos, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos?%s", user, opt.getURLQuery().Encode()), nil, nil, &repos)
}
// ListOrgReposOptions options for a organization's repositories
type ListOrgReposOptions struct {
ListOptions
} }
// 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) ([]*Repository, error) { func (c *Client) ListOrgRepos(org string, opt ListOrgReposOptions) ([]*Repository, error) {
repos := make([]*Repository, 0, 10) opt.setDefaults()
return repos, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos", org), nil, nil, &repos) repos := make([]*Repository, 0, opt.PageSize)
return repos, c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos?%s", org, opt.getURLQuery().Encode()), nil, nil, &repos)
}
// SearchRepoOptions options for searching repositories
type SearchRepoOptions struct {
ListOptions
Keyword string
Topic bool
IncludeDesc bool
UID int64
PriorityOwnerID int64
StarredBy int64
Private bool
Template bool
Mode string
Exclusive bool
Sort string
}
// QueryEncode turns options into querystring argument
func (opt *SearchRepoOptions) QueryEncode() string {
query := opt.getURLQuery()
if opt.Keyword != "" {
query.Add("q", opt.Keyword)
}
query.Add("topic", fmt.Sprintf("%t", opt.Topic))
query.Add("includeDesc", fmt.Sprintf("%t", opt.IncludeDesc))
if opt.UID > 0 {
query.Add("uid", fmt.Sprintf("%d", opt.UID))
}
if opt.PriorityOwnerID > 0 {
query.Add("priority_owner_id", fmt.Sprintf("%d", opt.PriorityOwnerID))
}
if opt.StarredBy > 0 {
query.Add("starredBy", fmt.Sprintf("%d", opt.StarredBy))
}
query.Add("private", fmt.Sprintf("%t", opt.Private))
query.Add("template", fmt.Sprintf("%t", opt.Template))
if opt.Mode != "" {
query.Add("mode", opt.Mode)
}
query.Add("exclusive", fmt.Sprintf("%t", opt.Exclusive))
if opt.Sort != "" {
query.Add("sort", opt.Sort)
}
return query.Encode()
}
type searchRepoResponse struct {
Repos []*Repository `json:"data"`
}
// SearchRepos searches for repositories matching the given filters
func (c *Client) SearchRepos(opt SearchRepoOptions) ([]*Repository, error) {
opt.setDefaults()
resp := new(searchRepoResponse)
link, _ := url.Parse("/repos/search")
link.RawQuery = opt.QueryEncode()
err := c.getParsedResponse("GET", link.String(), nil, nil, &resp)
return resp.Repos, err
} }
// CreateRepoOption options when creating repository // CreateRepoOption options when creating repository
@ -93,6 +177,8 @@ type CreateRepoOption struct {
License string `json:"license"` License string `json:"license"`
// Readme of the repository to create // Readme of the repository to create
Readme string `json:"readme"` Readme string `json:"readme"`
// DefaultBranch of the repository (used when initializes and in template)
DefaultBranch string `json:"default_branch"`
} }
// CreateRepo creates a repository for authenticated user. // CreateRepo creates a repository for authenticated user.

View File

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea 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.
@ -45,18 +46,46 @@ type PayloadCommitVerification struct {
// Branch represents a repository branch // Branch represents a repository branch
type Branch struct { type Branch struct {
Name string `json:"name"` Name string `json:"name"`
Commit *PayloadCommit `json:"commit"` Commit *PayloadCommit `json:"commit"`
Protected bool `json:"protected"`
RequiredApprovals int64 `json:"required_approvals"`
EnableStatusCheck bool `json:"enable_status_check"`
StatusCheckContexts []string `json:"status_check_contexts"`
UserCanPush bool `json:"user_can_push"`
UserCanMerge bool `json:"user_can_merge"`
EffectiveBranchProtectionName string `json:"effective_branch_protection_name"`
}
// ListRepoBranchesOptions options for listing a repository's branches
type ListRepoBranchesOptions struct {
ListOptions
} }
// ListRepoBranches list all the branches of one repository // ListRepoBranches list all the branches of one repository
func (c *Client) ListRepoBranches(user, repo string) ([]*Branch, error) { func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions) ([]*Branch, error) {
branches := make([]*Branch, 0, 10) opt.setDefaults()
return branches, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches", user, repo), nil, nil, &branches) branches := make([]*Branch, 0, opt.PageSize)
return branches, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &branches)
} }
// 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, error) { func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, error) {
b := new(Branch) b := new(Branch)
return b, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b) if err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b); err != nil {
return nil, err
}
return b, nil
}
// DeleteRepoBranch delete a branch in a repository
func (c *Client) DeleteRepoBranch(user, repo, branch string) (bool, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return false, err
}
status, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil)
if err != nil {
return false, err
}
return status == 204, nil
} }

View File

@ -0,0 +1,148 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"bytes"
"encoding/json"
"fmt"
"net/url"
"time"
)
// BranchProtection represents a branch protection for a repository
type BranchProtection struct {
BranchName string `json:"branch_name"`
EnablePush bool `json:"enable_push"`
EnablePushWhitelist bool `json:"enable_push_whitelist"`
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"`
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
EnableStatusCheck bool `json:"enable_status_check"`
StatusCheckContexts []string `json:"status_check_contexts"`
RequiredApprovals int64 `json:"required_approvals"`
EnableApprovalsWhitelist bool `json:"enable_approvals_whitelist"`
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
RequireSignedCommits bool `json:"require_signed_commits"`
ProtectedFilePatterns string `json:"protected_file_patterns"`
// swagger:strfmt date-time
Created time.Time `json:"created_at"`
// swagger:strfmt date-time
Updated time.Time `json:"updated_at"`
}
// CreateBranchProtectionOption options for creating a branch protection
type CreateBranchProtectionOption struct {
BranchName string `json:"branch_name"`
EnablePush bool `json:"enable_push"`
EnablePushWhitelist bool `json:"enable_push_whitelist"`
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"`
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
EnableStatusCheck bool `json:"enable_status_check"`
StatusCheckContexts []string `json:"status_check_contexts"`
RequiredApprovals int64 `json:"required_approvals"`
EnableApprovalsWhitelist bool `json:"enable_approvals_whitelist"`
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
RequireSignedCommits bool `json:"require_signed_commits"`
ProtectedFilePatterns string `json:"protected_file_patterns"`
}
// EditBranchProtectionOption options for editing a branch protection
type EditBranchProtectionOption struct {
EnablePush *bool `json:"enable_push"`
EnablePushWhitelist *bool `json:"enable_push_whitelist"`
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistDeployKeys *bool `json:"push_whitelist_deploy_keys"`
EnableMergeWhitelist *bool `json:"enable_merge_whitelist"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
EnableStatusCheck *bool `json:"enable_status_check"`
StatusCheckContexts []string `json:"status_check_contexts"`
RequiredApprovals *int64 `json:"required_approvals"`
EnableApprovalsWhitelist *bool `json:"enable_approvals_whitelist"`
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
BlockOnRejectedReviews *bool `json:"block_on_rejected_reviews"`
BlockOnOutdatedBranch *bool `json:"block_on_outdated_branch"`
DismissStaleApprovals *bool `json:"dismiss_stale_approvals"`
RequireSignedCommits *bool `json:"require_signed_commits"`
ProtectedFilePatterns *string `json:"protected_file_patterns"`
}
// ListBranchProtectionsOptions list branch protection options
type ListBranchProtectionsOptions struct {
ListOptions
}
// ListBranchProtections list branch protections for a repo
func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtectionsOptions) ([]*BranchProtection, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
bps := make([]*BranchProtection, 0, 5)
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/branch_protections", owner, repo))
link.RawQuery = opt.getURLQuery().Encode()
return bps, c.getParsedResponse("GET", link.String(), jsonHeader, nil, &bps)
}
// GetBranchProtection gets a branch protection
func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtection, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
bp := new(BranchProtection)
return bp, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, nil, bp)
}
// CreateBranchProtection creates a branch protection for a repo
func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProtectionOption) (*BranchProtection, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
bp := new(BranchProtection)
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
return bp, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/branch_protections", owner, repo), jsonHeader, bytes.NewReader(body), bp)
}
// EditBranchProtection edits a branch protection for a repo
func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchProtectionOption) (*BranchProtection, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
bp := new(BranchProtection)
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
return bp, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, bytes.NewReader(body), bp)
}
// DeleteBranchProtection deletes a branch protection for a repo
func (c *Client) DeleteBranchProtection(owner, repo, name string) error {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return err
}
_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/branch_protections/%s", owner, repo, name), jsonHeader, nil)
return err
}

View File

@ -10,20 +10,23 @@ import (
"fmt" "fmt"
) )
// ListCollaboratorsOptions options for listing a repository's collaborators
type ListCollaboratorsOptions struct {
ListOptions
}
// ListCollaborators list a repository's collaborators // ListCollaborators list a repository's collaborators
func (c *Client) ListCollaborators(user, repo string) ([]*User, error) { func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptions) ([]*User, error) {
collaborators := make([]*User, 0, 10) opt.setDefaults()
err := c.getParsedResponse("GET", collaborators := make([]*User, 0, opt.PageSize)
fmt.Sprintf("/repos/%s/%s/collaborators", user, repo), return collaborators, c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/collaborators?%s", user, repo, opt.getURLQuery().Encode()),
nil, nil, &collaborators) nil, nil, &collaborators)
return collaborators, err
} }
// 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, error) { func (c *Client) IsCollaborator(user, repo, collaborator string) (bool, error) {
status, err := c.getStatusCode("GET", status, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator),
nil, nil)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -44,14 +47,13 @@ func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollabo
if err != nil { if err != nil {
return err return err
} }
_, err = c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, bytes.NewReader(body)) _, err = c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), jsonHeader, bytes.NewReader(body))
return err return err
} }
// DeleteCollaborator remove a collaborator from a repository // DeleteCollaborator remove a collaborator from a repository
func (c *Client) DeleteCollaborator(user, repo, collaborator string) error { func (c *Client) DeleteCollaborator(user, repo, collaborator string) error {
_, err := c.getResponse("DELETE", _, err := c.getResponse("DELETE",
fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
nil, nil)
return err return err
} }

View File

@ -7,6 +7,8 @@ package gitea
import ( import (
"fmt" "fmt"
"net/url"
"time"
) )
// Identity for a person's identity like an author or committer // Identity for a person's identity like an author or committer
@ -46,8 +48,39 @@ type Commit struct {
Parents []*CommitMeta `json:"parents"` Parents []*CommitMeta `json:"parents"`
} }
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
type CommitDateOptions struct {
Author time.Time `json:"author"`
Committer time.Time `json:"committer"`
}
// GetSingleCommit returns a single commit // GetSingleCommit returns a single commit
func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, error) { func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, error) {
commit := new(Commit) commit := new(Commit)
return commit, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit) return commit, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit)
} }
// ListCommitOptions list commit options
type ListCommitOptions struct {
ListOptions
//SHA or branch to start listing commits from (usually 'master')
SHA string
}
// QueryEncode turns options into querystring argument
func (opt *ListCommitOptions) QueryEncode() string {
query := opt.ListOptions.getURLQuery()
if opt.SHA != "" {
query.Add("sha", opt.SHA)
}
return query.Encode()
}
// ListRepoCommits return list of commits from a repo
func (c *Client) ListRepoCommits(user, repo string, opt ListCommitOptions) ([]*Commit, error) {
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/commits", user, repo))
opt.setDefaults()
commits := make([]*Commit, 0, opt.PageSize)
link.RawQuery = opt.QueryEncode()
return commits, c.getParsedResponse("GET", link.String(), nil, nil, &commits)
}

View File

@ -6,11 +6,187 @@
package gitea package gitea
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
) )
// FileOptions options for all file APIs
type FileOptions struct {
// message (optional) for the commit of this file. if not supplied, a default message will be used
Message string `json:"message"`
// branch (optional) to base this file from. if not given, the default branch is used
BranchName string `json:"branch"`
// new_branch (optional) will make a new branch from `branch` before creating the file
NewBranchName string `json:"new_branch"`
// `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
Author Identity `json:"author"`
Committer Identity `json:"committer"`
Dates CommitDateOptions `json:"dates"`
}
// CreateFileOptions options for creating files
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
type CreateFileOptions struct {
FileOptions
// content must be base64 encoded
// required: true
Content string `json:"content"`
}
// DeleteFileOptions options for deleting files (used for other File structs below)
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
type DeleteFileOptions struct {
FileOptions
// sha is the SHA for the file that already exists
// required: true
SHA string `json:"sha"`
}
// UpdateFileOptions options for updating files
// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
type UpdateFileOptions struct {
FileOptions
// sha is the SHA for the file that already exists
// required: true
SHA string `json:"sha"`
// content must be base64 encoded
// required: true
Content string `json:"content"`
// from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL
FromPath string `json:"from_path"`
}
// FileLinksResponse contains the links for a repo's file
type FileLinksResponse struct {
Self *string `json:"self"`
GitURL *string `json:"git"`
HTMLURL *string `json:"html"`
}
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
type ContentsResponse struct {
Name string `json:"name"`
Path string `json:"path"`
SHA string `json:"sha"`
// `type` will be `file`, `dir`, `symlink`, or `submodule`
Type string `json:"type"`
Size int64 `json:"size"`
// `encoding` is populated when `type` is `file`, otherwise null
Encoding *string `json:"encoding"`
// `content` is populated when `type` is `file`, otherwise null
Content *string `json:"content"`
// `target` is populated when `type` is `symlink`, otherwise null
Target *string `json:"target"`
URL *string `json:"url"`
HTMLURL *string `json:"html_url"`
GitURL *string `json:"git_url"`
DownloadURL *string `json:"download_url"`
// `submodule_git_url` is populated when `type` is `submodule`, otherwise null
SubmoduleGitURL *string `json:"submodule_git_url"`
Links *FileLinksResponse `json:"_links"`
}
// FileCommitResponse contains information generated from a Git commit for a repo's file.
type FileCommitResponse struct {
CommitMeta
HTMLURL string `json:"html_url"`
Author *CommitUser `json:"author"`
Committer *CommitUser `json:"committer"`
Parents []*CommitMeta `json:"parents"`
Message string `json:"message"`
Tree *CommitMeta `json:"tree"`
}
// FileResponse contains information about a repo's file
type FileResponse struct {
Content *ContentsResponse `json:"content"`
Commit *FileCommitResponse `json:"commit"`
Verification *PayloadCommitVerification `json:"verification"`
}
// FileDeleteResponse contains information about a repo's file that was deleted
type FileDeleteResponse struct {
Content interface{} `json:"content"` // to be set to nil
Commit *FileCommitResponse `json:"commit"`
Verification *PayloadCommitVerification `json:"verification"`
}
// 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, tree -> macaron.go(no leading slash) // e.g.: ref -> master, tree -> macaron.go(no leading slash)
func (c *Client) GetFile(user, repo, ref, tree string) ([]byte, error) { func (c *Client) GetFile(user, repo, ref, tree string) ([]byte, error) {
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s/%s", user, repo, ref, tree), nil, nil) return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s/%s", user, repo, ref, tree), nil, nil)
} }
// GetContents get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
// ref is optional
func (c *Client) GetContents(owner, repo, ref, filepath string) (*ContentsResponse, error) {
cr := new(ContentsResponse)
return cr, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, filepath, ref), jsonHeader, nil, cr)
}
// CreateFile create a file in a repository
func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions) (*FileResponse, error) {
var err error
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
return nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
fr := new(FileResponse)
return fr, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body), fr)
}
// UpdateFile update a file in a repository
func (c *Client) UpdateFile(owner, repo, filepath string, opt UpdateFileOptions) (*FileResponse, error) {
var err error
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
return nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
fr := new(FileResponse)
return fr, c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body), fr)
}
// DeleteFile delete a file from repository
func (c *Client) DeleteFile(owner, repo, filepath string, opt DeleteFileOptions) error {
var err error
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
return err
}
body, err := json.Marshal(&opt)
if err != nil {
return err
}
status, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body))
if err != nil {
return err
}
if status != 200 && status != 204 {
return fmt.Errorf("unexpected Status: %d", status)
}
return nil
}
func (c *Client) setDefaultBranchForOldVersions(owner, repo, branch string) (string, error) {
if len(branch) == 0 {
// Gitea >= 1.12.0 Use DefaultBranch on "", mimic this for older versions
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
r, err := c.GetRepo(owner, repo)
if err != nil {
return "", err
}
return r.DefaultBranch, nil
}
}
return branch, nil
}

View File

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"time" "time"
) )
@ -24,10 +25,32 @@ type DeployKey struct {
Repository *Repository `json:"repository,omitempty"` Repository *Repository `json:"repository,omitempty"`
} }
// ListDeployKeysOptions options for listing a repository's deploy keys
type ListDeployKeysOptions struct {
ListOptions
KeyID int64
Fingerprint string
}
// QueryEncode turns options into querystring argument
func (opt *ListDeployKeysOptions) QueryEncode() string {
query := opt.getURLQuery()
if opt.KeyID > 0 {
query.Add("key_id", fmt.Sprintf("%d", opt.KeyID))
}
if len(opt.Fingerprint) > 0 {
query.Add("fingerprint", opt.Fingerprint)
}
return query.Encode()
}
// 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) ([]*DeployKey, error) { func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([]*DeployKey, error) {
keys := make([]*DeployKey, 0, 10) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/keys", user, repo))
return keys, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys", user, repo), nil, nil, &keys) opt.setDefaults()
link.RawQuery = opt.QueryEncode()
keys := make([]*DeployKey, 0, opt.PageSize)
return keys, c.getParsedResponse("GET", link.String(), nil, nil, &keys)
} }
// GetDeployKey get one deploy key with key id // GetDeployKey get one deploy key with key id

View File

@ -17,8 +17,14 @@ type Tag struct {
TarballURL string `json:"tarball_url"` TarballURL string `json:"tarball_url"`
} }
// ListRepoTags list all the branches of one repository // ListRepoTagsOptions options for listing a repository's tags
func (c *Client) ListRepoTags(user, repo string) ([]*Tag, error) { type ListRepoTagsOptions struct {
tags := make([]*Tag, 0, 10) ListOptions
return tags, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags", user, repo), nil, nil, &tags) }
// ListRepoTags list all the branches of one repository
func (c *Client) ListRepoTags(user, repo string, opt ListRepoTagsOptions) ([]*Tag, error) {
opt.setDefaults()
tags := make([]*Tag, 0, opt.PageSize)
return tags, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &tags)
} }

58
vendor/code.gitea.io/sdk/gitea/repo_topics.go generated vendored Normal file
View File

@ -0,0 +1,58 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"bytes"
"encoding/json"
"fmt"
)
// ListRepoTopicsOptions options for listing repo's topics
type ListRepoTopicsOptions struct {
ListOptions
}
// topicsList represents a list of repo's topics
type topicsList struct {
Topics []string `json:"topics"`
}
// ListRepoTopics list all repository's topics
func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([]string, error) {
opt.setDefaults()
list := new(topicsList)
err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/topics?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, list)
if err != nil {
return nil, err
}
return list.Topics, nil
}
// SetRepoTopics replaces the list of repo's topics
func (c *Client) SetRepoTopics(user, repo string, list []string) error {
l := topicsList{Topics: list}
body, err := json.Marshal(&l)
if err != nil {
return err
}
_, err = c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics", user, repo), jsonHeader, bytes.NewReader(body))
return err
}
// AddRepoTopic adds a topic to a repo's topics list
func (c *Client) AddRepoTopic(user, repo, topic string) error {
_, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
return err
}
// DeleteRepoTopic deletes a topic from repo's topics list
func (c *Client) DeleteRepoTopic(user, repo, topic string) error {
_, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
return err
}

32
vendor/code.gitea.io/sdk/gitea/repo_transfer.go generated vendored Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"bytes"
"encoding/json"
"fmt"
)
// TransferRepoOption options when transfer a repository's ownership
type TransferRepoOption struct {
// required: true
NewOwner string `json:"new_owner"`
// ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories.
TeamIDs *[]int64 `json:"team_ids"`
}
// TransferRepo transfers the ownership of a repository
func (c *Client) TransferRepo(owner, reponame string, opt TransferRepoOption) (*Repository, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
repo := new(Repository)
return repo, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/transfer", owner, reponame), jsonHeader, bytes.NewReader(body), repo)
}

View File

@ -31,11 +31,10 @@ 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, error) { func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, error) {
var trees 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 {
path += "?recursive=1" path += "?recursive=1"
} }
err := c.getParsedResponse("GET", path, nil, nil, &trees) return trees, c.getParsedResponse("GET", path, nil, nil, trees)
return &trees, err
} }

View File

@ -21,21 +21,53 @@ type WatchInfo struct {
} }
// GetWatchedRepos list all the watched repos of user // GetWatchedRepos list all the watched repos of user
func (c *Client) GetWatchedRepos(user, pass string) ([]*Repository, error) { func (c *Client) GetWatchedRepos(user string) ([]*Repository, error) {
repos := make([]*Repository, 0, 10) repos := make([]*Repository, 0, 10)
return repos, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), return repos, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), nil, nil, &repos)
http.Header{"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, nil, &repos) }
// GetMyWatchedRepos list repositories watched by the authenticated user
func (c *Client) GetMyWatchedRepos() ([]*Repository, error) {
repos := make([]*Repository, 0, 10)
return repos, c.getParsedResponse("GET", fmt.Sprintf("/user/subscriptions"), nil, nil, &repos)
}
// CheckRepoWatch check if the current user is watching a repo
func (c *Client) CheckRepoWatch(repoUser, repoName string) (bool, error) {
status, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil)
if err != nil {
return false, err
}
switch status {
case http.StatusNotFound:
return false, nil
case http.StatusOK:
return true, nil
default:
return false, fmt.Errorf("unexpected Status: %d", status)
}
} }
// WatchRepo start to watch a repository // WatchRepo start to watch a repository
func (c *Client) WatchRepo(user, pass, repoUser, repoName string) (*WatchInfo, error) { func (c *Client) WatchRepo(repoUser, repoName string) error {
i := new(WatchInfo) status, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil)
return i, c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), if err != nil {
http.Header{"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, nil, i) return err
}
if status == http.StatusOK {
return nil
}
return fmt.Errorf("unexpected Status: %d", status)
} }
// UnWatchRepo start to watch a repository // UnWatchRepo stop to watch a repository
func (c *Client) UnWatchRepo(user, pass, repoUser, repoName string) (int, error) { func (c *Client) UnWatchRepo(repoUser, repoName string) error {
return c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), status, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil)
http.Header{"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, nil) if err != nil {
return err
}
if status == http.StatusNoContent {
return nil
}
return fmt.Errorf("unexpected Status: %d", status)
} }

View File

@ -50,29 +50,25 @@ type CreateStatusOption struct {
} }
// CreateStatus creates a new Status for a given Commit // CreateStatus creates a new Status for a given Commit
//
// POST /repos/:owner/:repo/statuses/:sha
func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, error) { func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, error) {
body, err := json.Marshal(&opts) body, err := json.Marshal(&opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
status := &Status{} status := new(Status)
return status, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, sha), return status, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, sha), jsonHeader, bytes.NewReader(body), status)
jsonHeader, bytes.NewReader(body), status)
} }
// ListStatusesOption holds pagination information // ListStatusesOption options for listing a repository's commit's statuses
type ListStatusesOption struct { type ListStatusesOption struct {
Page int ListOptions
} }
// ListStatuses returns all statuses for a given Commit // ListStatuses returns all statuses for a given Commit
// func (c *Client) ListStatuses(owner, repo, sha string, opt ListStatusesOption) ([]*Status, error) {
// GET /repos/:owner/:repo/commits/:ref/statuses opt.setDefaults()
func (c *Client) ListStatuses(owner, repo, sha string, opts ListStatusesOption) ([]*Status, error) { statuses := make([]*Status, 0, opt.PageSize)
statuses := make([]*Status, 0, 10) return statuses, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?%s", owner, repo, sha, opt.getURLQuery().Encode()), nil, nil, &statuses)
return statuses, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?page=%d", owner, repo, sha, opts.Page), nil, nil, &statuses)
} }
// CombinedStatus holds the combined state of several statuses for a single commit // CombinedStatus holds the combined state of several statuses for a single commit
@ -87,9 +83,7 @@ type CombinedStatus struct {
} }
// GetCombinedStatus returns the CombinedStatus for a given Commit // GetCombinedStatus returns the CombinedStatus for a given Commit
//
// GET /repos/:owner/:repo/commits/:ref/status
func (c *Client) GetCombinedStatus(owner, repo, sha string) (*CombinedStatus, error) { func (c *Client) GetCombinedStatus(owner, repo, sha string) (*CombinedStatus, error) {
status := &CombinedStatus{} status := new(CombinedStatus)
return status, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, sha), nil, nil, status) return status, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, sha), nil, nil, status)
} }

View File

@ -7,17 +7,10 @@ package gitea
import ( import (
"bytes" "bytes"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
) )
// BasicAuthEncode generate base64 of basic auth head
func BasicAuthEncode(user, pass string) string {
return base64.StdEncoding.EncodeToString([]byte(user + ":" + pass))
}
// AccessToken represents an API access token. // AccessToken represents an API access token.
type AccessToken struct { type AccessToken struct {
ID int64 `json:"id"` ID int64 `json:"id"`
@ -26,11 +19,19 @@ type AccessToken struct {
TokenLastEight string `json:"token_last_eight"` TokenLastEight string `json:"token_last_eight"`
} }
// ListAccessTokens lista all the access tokens of user // ListAccessTokensOptions options for listing a users's access tokens
func (c *Client) ListAccessTokens(user, pass string) ([]*AccessToken, error) { type ListAccessTokensOptions struct {
tokens := make([]*AccessToken, 0, 10) ListOptions
return tokens, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens", user), }
http.Header{"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}}, nil, &tokens)
// ListAccessTokens lists all the access tokens of user
func (c *Client) ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken, error) {
if len(c.username) == 0 {
return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
}
opts.setDefaults()
tokens := make([]*AccessToken, 0, opts.PageSize)
return tokens, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", c.username, opts.getURLQuery().Encode()), jsonHeader, nil, &tokens)
} }
// CreateAccessTokenOption options when create access token // CreateAccessTokenOption options when create access token
@ -39,21 +40,23 @@ type CreateAccessTokenOption struct {
} }
// CreateAccessToken create one access token with options // CreateAccessToken create one access token with options
func (c *Client) CreateAccessToken(user, pass string, opt CreateAccessTokenOption) (*AccessToken, error) { func (c *Client) CreateAccessToken(opt CreateAccessTokenOption) (*AccessToken, error) {
if len(c.username) == 0 {
return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
t := new(AccessToken) t := new(AccessToken)
return t, c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", user), return t, c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", c.username), jsonHeader, bytes.NewReader(body), t)
http.Header{
"content-type": []string{"application/json"},
"Authorization": []string{"Basic " + BasicAuthEncode(user, pass)}},
bytes.NewReader(body), t)
} }
// DeleteAccessToken delete token with key id // DeleteAccessToken delete token with key id
func (c *Client) DeleteAccessToken(user string, keyID int64) error { func (c *Client) DeleteAccessToken(keyID int64) error {
_, err := c.getResponse("DELETE", fmt.Sprintf("/user/%s/tokens/%d", user, keyID), nil, nil) if len(c.username) == 0 {
return fmt.Errorf("\"username\" not set: only BasicAuth allowed")
}
_, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%d", c.username, keyID), jsonHeader, nil)
return err return err
} }

View File

@ -7,6 +7,7 @@ package gitea
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
) )
// Email an email address belonging to a user // Email an email address belonging to a user
@ -16,10 +17,16 @@ type Email struct {
Primary bool `json:"primary"` Primary bool `json:"primary"`
} }
// ListEmailsOptions options for listing current's user emails
type ListEmailsOptions struct {
ListOptions
}
// ListEmails all the email addresses of user // ListEmails all the email addresses of user
func (c *Client) ListEmails() ([]*Email, error) { func (c *Client) ListEmails(opt ListEmailsOptions) ([]*Email, error) {
emails := make([]*Email, 0, 3) opt.setDefaults()
return emails, c.getParsedResponse("GET", "/user/emails", nil, nil, &emails) emails := make([]*Email, 0, opt.PageSize)
return emails, c.getParsedResponse("GET", fmt.Sprintf("/user/emails?%s", opt.getURLQuery().Encode()), nil, nil, &emails)
} }
// CreateEmailOption options when creating email addresses // CreateEmailOption options when creating email addresses
@ -35,7 +42,7 @@ func (c *Client) AddEmail(opt CreateEmailOption) ([]*Email, error) {
return nil, err return nil, err
} }
emails := make([]*Email, 0, 3) emails := make([]*Email, 0, 3)
return emails, c.getParsedResponse("POST", "/user/emails", jsonHeader, bytes.NewReader(body), emails) return emails, c.getParsedResponse("POST", "/user/emails", jsonHeader, bytes.NewReader(body), &emails)
} }
// DeleteEmailOption options when deleting email addresses // DeleteEmailOption options when deleting email addresses

View File

@ -6,28 +6,42 @@ package gitea
import "fmt" import "fmt"
// ListFollowersOptions options for listing followers
type ListFollowersOptions struct {
ListOptions
}
// ListMyFollowers list all the followers of current user // ListMyFollowers list all the followers of current user
func (c *Client) ListMyFollowers(page int) ([]*User, error) { func (c *Client) ListMyFollowers(opt ListFollowersOptions) ([]*User, error) {
users := make([]*User, 0, 10) opt.setDefaults()
return users, c.getParsedResponse("GET", fmt.Sprintf("/user/followers?page=%d", page), nil, nil, &users) users := make([]*User, 0, opt.PageSize)
return users, c.getParsedResponse("GET", fmt.Sprintf("/user/followers?%s", opt.getURLQuery().Encode()), nil, nil, &users)
} }
// ListFollowers list all the followers of one user // ListFollowers list all the followers of one user
func (c *Client) ListFollowers(user string, page int) ([]*User, error) { func (c *Client) ListFollowers(user string, opt ListFollowersOptions) ([]*User, error) {
users := make([]*User, 0, 10) opt.setDefaults()
return users, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?page=%d", user, page), nil, nil, &users) users := make([]*User, 0, opt.PageSize)
return users, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
}
// ListFollowingOptions options for listing a user's users being followed
type ListFollowingOptions struct {
ListOptions
} }
// ListMyFollowing list all the users current user followed // ListMyFollowing list all the users current user followed
func (c *Client) ListMyFollowing(page int) ([]*User, error) { func (c *Client) ListMyFollowing(opt ListFollowingOptions) ([]*User, error) {
users := make([]*User, 0, 10) opt.setDefaults()
return users, c.getParsedResponse("GET", fmt.Sprintf("/user/following?page=%d", page), nil, nil, &users) users := make([]*User, 0, opt.PageSize)
return users, c.getParsedResponse("GET", fmt.Sprintf("/user/following?%s", opt.getURLQuery().Encode()), nil, nil, &users)
} }
// ListFollowing list all the users the user followed // ListFollowing list all the users the user followed
func (c *Client) ListFollowing(user string, page int) ([]*User, error) { func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User, error) {
users := make([]*User, 0, 10) opt.setDefaults()
return users, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?page=%d", user, page), nil, nil, &users) users := make([]*User, 0, opt.PageSize)
return users, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
} }
// IsFollowing if current user followed the target // IsFollowing if current user followed the target

View File

@ -33,16 +33,23 @@ type GPGKeyEmail struct {
Verified bool `json:"verified"` Verified bool `json:"verified"`
} }
// ListGPGKeysOptions options for listing a user's GPGKeys
type ListGPGKeysOptions struct {
ListOptions
}
// ListGPGKeys list all the GPG keys of the user // ListGPGKeys list all the GPG keys of the user
func (c *Client) ListGPGKeys(user string) ([]*GPGKey, error) { func (c *Client) ListGPGKeys(user string, opt ListGPGKeysOptions) ([]*GPGKey, error) {
keys := make([]*GPGKey, 0, 10) opt.setDefaults()
return keys, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys", user), nil, nil, &keys) keys := make([]*GPGKey, 0, opt.PageSize)
return keys, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)
} }
// ListMyGPGKeys list all the GPG keys of current user // ListMyGPGKeys list all the GPG keys of current user
func (c *Client) ListMyGPGKeys() ([]*GPGKey, error) { func (c *Client) ListMyGPGKeys(opt *ListGPGKeysOptions) ([]*GPGKey, error) {
keys := make([]*GPGKey, 0, 10) opt.setDefaults()
return keys, c.getParsedResponse("GET", "/user/gpg_keys", nil, nil, &keys) keys := make([]*GPGKey, 0, opt.PageSize)
return keys, c.getParsedResponse("GET", fmt.Sprintf("/user/gpg_keys?%s", opt.getURLQuery().Encode()), nil, nil, &keys)
} }
// GetGPGKey get current user's GPG key by key id // GetGPGKey get current user's GPG key by key id

View File

@ -24,16 +24,23 @@ type PublicKey struct {
KeyType string `json:"key_type,omitempty"` KeyType string `json:"key_type,omitempty"`
} }
// ListPublicKeysOptions options for listing a user's PublicKeys
type ListPublicKeysOptions struct {
ListOptions
}
// ListPublicKeys list all the public keys of the user // ListPublicKeys list all the public keys of the user
func (c *Client) ListPublicKeys(user string) ([]*PublicKey, error) { func (c *Client) ListPublicKeys(user string, opt ListPublicKeysOptions) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 10) opt.setDefaults()
return keys, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys", user), nil, nil, &keys) keys := make([]*PublicKey, 0, opt.PageSize)
return keys, c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)
} }
// ListMyPublicKeys list all the public keys of current user // ListMyPublicKeys list all the public keys of current user
func (c *Client) ListMyPublicKeys() ([]*PublicKey, error) { func (c *Client) ListMyPublicKeys(opt ListPublicKeysOptions) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 10) opt.setDefaults()
return keys, c.getParsedResponse("GET", "/user/keys", nil, nil, &keys) keys := make([]*PublicKey, 0, opt.PageSize)
return keys, c.getParsedResponse("GET", fmt.Sprintf("/user/keys?%s", opt.getURLQuery().Encode()), nil, nil, &keys)
} }
// GetPublicKey get current user's public key by key id // GetPublicKey get current user's public key by key id

View File

@ -1,14 +1,44 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea package gitea
import "fmt" import (
"fmt"
"net/url"
)
type searchUsersResponse struct { type searchUsersResponse struct {
Users []*User `json:"data"` Users []*User `json:"data"`
} }
// SearchUsersOption options for SearchUsers
type SearchUsersOption struct {
ListOptions
KeyWord string
}
// QueryEncode turns options into querystring argument
func (opt *SearchUsersOption) QueryEncode() string {
query := make(url.Values)
if opt.Page > 0 {
query.Add("page", fmt.Sprintf("%d", opt.Page))
}
if opt.PageSize > 0 {
query.Add("limit", fmt.Sprintf("%d", opt.PageSize))
}
if len(opt.KeyWord) > 0 {
query.Add("q", opt.KeyWord)
}
return query.Encode()
}
// SearchUsers finds users by query // SearchUsers finds users by query
func (c *Client) SearchUsers(query string, limit int) ([]*User, error) { func (c *Client) SearchUsers(opt SearchUsersOption) ([]*User, error) {
link, _ := url.Parse("/users/search")
link.RawQuery = opt.QueryEncode()
resp := new(searchUsersResponse) resp := new(searchUsersResponse)
err := c.getParsedResponse("GET", fmt.Sprintf("/users/search?q=%s&limit=%d", query, limit), nil, nil, &resp) err := c.getParsedResponse("GET", link.String(), nil, nil, &resp)
return resp.Users, err return resp.Users, err
} }

57
vendor/code.gitea.io/sdk/gitea/version.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"fmt"
"github.com/hashicorp/go-version"
)
// ServerVersion returns the version of the server
func (c *Client) ServerVersion() (string, error) {
var v = struct {
Version string `json:"version"`
}{}
return v.Version, c.getParsedResponse("GET", "/version", nil, nil, &v)
}
// CheckServerVersionConstraint validates that the login's server satisfies a
// given version constraint such as ">= 1.11.0+dev"
func (c *Client) CheckServerVersionConstraint(constraint string) error {
c.versionLock.RLock()
if c.serverVersion == nil {
c.versionLock.RUnlock()
if err := c.loadClientServerVersion(); err != nil {
return err
}
} else {
c.versionLock.RUnlock()
}
check, err := version.NewConstraint(constraint)
if err != nil {
return err
}
if !check.Check(c.serverVersion) {
return fmt.Errorf("gitea server at %s does not satisfy version constraint %s", c.url, constraint)
}
return nil
}
// loadClientServerVersion init the serverVersion variable
func (c *Client) loadClientServerVersion() error {
c.versionLock.Lock()
defer c.versionLock.Unlock()
raw, err := c.ServerVersion()
if err != nil {
return err
}
if c.serverVersion, err = version.NewVersion(raw); err != nil {
return err
}
return nil
}

5
vendor/gitea.com/jolheiser/gitea-vet/.gitignore generated vendored Normal file
View File

@ -0,0 +1,5 @@
# GoLand
.idea/
# Binaries
/gitea-vet*

19
vendor/gitea.com/jolheiser/gitea-vet/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2020 The Gitea Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

7
vendor/gitea.com/jolheiser/gitea-vet/Makefile generated vendored Normal file
View File

@ -0,0 +1,7 @@
.PHONY: build
build:
go build
.PHONY: fmt
fmt:
go fmt ./...

7
vendor/gitea.com/jolheiser/gitea-vet/README.md generated vendored Normal file
View File

@ -0,0 +1,7 @@
# gitea-vet
`go vet` tool for Gitea
| Analyzer | Description |
|----------|---------------------------------------------------------------------|
| Imports | Checks for import sorting. stdlib->code.gitea.io->other |
| License | Checks file headers for some form of `Copyright...YYYY...Gitea/Gogs`|

54
vendor/gitea.com/jolheiser/gitea-vet/checks/imports.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package checks
import (
"strings"
"golang.org/x/tools/go/analysis"
)
var Imports = &analysis.Analyzer{
Name: "imports",
Doc: "check for import order.",
Run: runImports,
}
func runImports(pass *analysis.Pass) (interface{}, error) {
for _, file := range pass.Files {
level := 0
for _, im := range file.Imports {
var lvl int
val := im.Path.Value
if importHasPrefix(val, "code.gitea.io") {
lvl = 2
} else if strings.Contains(val, ".") {
lvl = 3
} else {
lvl = 1
}
if lvl < level {
pass.Reportf(file.Pos(), "Imports are sorted wrong")
break
}
level = lvl
}
}
return nil, nil
}
func importHasPrefix(s, p string) bool {
return strings.HasPrefix(s, "\""+p)
}
func sliceHasPrefix(s string, prefixes ...string) bool {
for _, p := range prefixes {
if importHasPrefix(s, p) {
return true
}
}
return false
}

73
vendor/gitea.com/jolheiser/gitea-vet/checks/license.go generated vendored Normal file
View File

@ -0,0 +1,73 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package checks
import (
"regexp"
"strings"
"golang.org/x/tools/go/analysis"
)
var (
header = regexp.MustCompile(`.*Copyright.*\d{4}.*(Gitea|Gogs)`)
goGenerate = "//go:generate"
buildTag = "// +build"
)
var License = &analysis.Analyzer{
Name: "license",
Doc: "check for a copyright header.",
Run: runLicense,
}
func runLicense(pass *analysis.Pass) (interface{}, error) {
for _, file := range pass.Files {
if len(file.Comments) == 0 {
pass.Reportf(file.Pos(), "Copyright not found")
continue
}
if len(file.Comments[0].List) == 0 {
pass.Reportf(file.Pos(), "Copyright not found or wrong")
continue
}
commentGroup := 0
if strings.HasPrefix(file.Comments[0].List[0].Text, goGenerate) {
if len(file.Comments[0].List) > 1 {
pass.Reportf(file.Pos(), "Must be an empty line between the go:generate and the Copyright")
continue
}
commentGroup++
}
if strings.HasPrefix(file.Comments[0].List[0].Text, buildTag) {
commentGroup++
}
if len(file.Comments) < commentGroup+1 {
pass.Reportf(file.Pos(), "Copyright not found")
continue
}
if len(file.Comments[commentGroup].List) < 1 {
pass.Reportf(file.Pos(), "Copyright not found or wrong")
continue
}
var check bool
for _, comment := range file.Comments[commentGroup].List {
if header.MatchString(comment.Text) {
check = true
}
}
if !check {
pass.Reportf(file.Pos(), "Copyright did not match check")
}
}
return nil, nil
}

5
vendor/gitea.com/jolheiser/gitea-vet/go.mod generated vendored Normal file
View File

@ -0,0 +1,5 @@
module gitea.com/jolheiser/gitea-vet
go 1.14
require golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224

20
vendor/gitea.com/jolheiser/gitea-vet/go.sum generated vendored Normal file
View File

@ -0,0 +1,20 @@
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 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

17
vendor/gitea.com/jolheiser/gitea-vet/main.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main
import (
"gitea.com/jolheiser/gitea-vet/checks"
"golang.org/x/tools/go/analysis/unitchecker"
)
func main() {
unitchecker.Main(
checks.Imports,
checks.License,
)
}

14
vendor/github.com/araddon/dateparse/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,14 @@
language: go
go:
- 1.10.x
- 1.11.x
before_install:
- go get -t -v ./...
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)

21
vendor/github.com/araddon/dateparse/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015-2017 Aaron Raddon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

282
vendor/github.com/araddon/dateparse/README.md generated vendored Normal file
View File

@ -0,0 +1,282 @@
Go Date Parser
---------------------------
Parse many date strings without knowing format in advance. Uses a scanner to read bytes and use a state machine to find format. Much faster than shotgun based parse methods. See [bench_test.go](https://github.com/araddon/dateparse/blob/master/bench_test.go) for performance comparison.
[![Code Coverage](https://codecov.io/gh/araddon/dateparse/branch/master/graph/badge.svg)](https://codecov.io/gh/araddon/dateparse)
[![GoDoc](https://godoc.org/github.com/araddon/dateparse?status.svg)](http://godoc.org/github.com/araddon/dateparse)
[![Build Status](https://travis-ci.org/araddon/dateparse.svg?branch=master)](https://travis-ci.org/araddon/dateparse)
[![Go ReportCard](https://goreportcard.com/badge/araddon/dateparse)](https://goreportcard.com/report/araddon/dateparse)
**MM/DD/YYYY VS DD/MM/YYYY** Right now this uses mm/dd/yyyy WHEN ambiguous if this is not desired behavior, use `ParseStrict` which will fail on ambiguous date strings.
**Timezones** The location your server is configured affects the results! See example or https://play.golang.org/p/IDHRalIyXh and last paragraph here https://golang.org/pkg/time/#Parse.
```go
// Normal parse. Equivalent Timezone rules as time.Parse()
t, err := dateparse.ParseAny("3/1/2014")
// Parse Strict, error on ambigous mm/dd vs dd/mm dates
t, err := dateparse.ParseStrict("3/1/2014")
> returns error
// Return a string that represents the layout to parse the given date-time.
layout, err := dateparse.ParseFormat("May 8, 2009 5:57:51 PM")
> "Jan 2, 2006 3:04:05 PM"
```
cli tool for testing dateformats
----------------------------------
[Date Parse CLI](https://github.com/araddon/dateparse/blob/master/dateparse)
Extended example
-------------------
https://github.com/araddon/dateparse/blob/master/example/main.go
```go
package main
import (
"flag"
"fmt"
"time"
"github.com/scylladb/termtables"
"github.com/araddon/dateparse"
)
var examples = []string{
"May 8, 2009 5:57:51 PM",
"oct 7, 1970",
"oct 7, '70",
"oct. 7, 1970",
"oct. 7, 70",
"Mon Jan 2 15:04:05 2006",
"Mon Jan 2 15:04:05 MST 2006",
"Mon Jan 02 15:04:05 -0700 2006",
"Monday, 02-Jan-06 15:04:05 MST",
"Mon, 02 Jan 2006 15:04:05 MST",
"Tue, 11 Jul 2017 16:28:13 +0200 (CEST)",
"Mon, 02 Jan 2006 15:04:05 -0700",
"Thu, 4 Jan 2018 17:53:36 +0000",
"Mon Aug 10 15:44:11 UTC+0100 2015",
"Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
"September 17, 2012 10:09am",
"September 17, 2012 at 10:09am PST-08",
"September 17, 2012, 10:10:09",
"October 7, 1970",
"October 7th, 1970",
"12 Feb 2006, 19:17",
"12 Feb 2006 19:17",
"7 oct 70",
"7 oct 1970",
"03 February 2013",
"1 July 2013",
"2013-Feb-03",
// mm/dd/yy
"3/31/2014",
"03/31/2014",
"08/21/71",
"8/1/71",
"4/8/2014 22:05",
"04/08/2014 22:05",
"4/8/14 22:05",
"04/2/2014 03:00:51",
"8/8/1965 12:00:00 AM",
"8/8/1965 01:00:01 PM",
"8/8/1965 01:00 PM",
"8/8/1965 1:00 PM",
"8/8/1965 12:00 AM",
"4/02/2014 03:00:51",
"03/19/2012 10:11:59",
"03/19/2012 10:11:59.3186369",
// yyyy/mm/dd
"2014/3/31",
"2014/03/31",
"2014/4/8 22:05",
"2014/04/08 22:05",
"2014/04/2 03:00:51",
"2014/4/02 03:00:51",
"2012/03/19 10:11:59",
"2012/03/19 10:11:59.3186369",
// Chinese
"2014年04月08日",
// yyyy-mm-ddThh
"2006-01-02T15:04:05+0000",
"2009-08-12T22:15:09-07:00",
"2009-08-12T22:15:09",
"2009-08-12T22:15:09Z",
// yyyy-mm-dd hh:mm:ss
"2014-04-26 17:24:37.3186369",
"2012-08-03 18:31:59.257000000",
"2014-04-26 17:24:37.123",
"2013-04-01 22:43",
"2013-04-01 22:43:22",
"2014-12-16 06:20:00 UTC",
"2014-12-16 06:20:00 GMT",
"2014-04-26 05:24:37 PM",
"2014-04-26 13:13:43 +0800",
"2014-04-26 13:13:43 +0800 +08",
"2014-04-26 13:13:44 +09:00",
"2012-08-03 18:31:59.257000000 +0000 UTC",
"2015-09-30 18:48:56.35272715 +0000 UTC",
"2015-02-18 00:12:00 +0000 GMT",
"2015-02-18 00:12:00 +0000 UTC",
"2015-02-08 03:02:00 +0300 MSK m=+0.000000001",
"2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001",
"2017-07-19 03:21:51+00:00",
"2014-04-26",
"2014-04",
"2014",
"2014-05-11 08:20:13,787",
// mm.dd.yy
"3.31.2014",
"03.31.2014",
"08.21.71",
"2014.03",
"2014.03.30",
// yyyymmdd and similar
"20140601",
"20140722105203",
// unix seconds, ms, micro, nano
"1332151919",
"1384216367189",
"1384216367111222",
"1384216367111222333",
}
var (
timezone = ""
)
func main() {
flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone")
flag.Parse()
if timezone != "" {
// NOTE: This is very, very important to understand
// time-parsing in go
loc, err := time.LoadLocation(timezone)
if err != nil {
panic(err.Error())
}
time.Local = loc
}
table := termtables.CreateTable()
table.AddHeaders("Input", "Parsed, and Output as %v")
for _, dateExample := range examples {
t, err := dateparse.ParseLocal(dateExample)
if err != nil {
panic(err.Error())
}
table.AddRow(dateExample, fmt.Sprintf("%v", t))
}
fmt.Println(table.Render())
}
/*
+-------------------------------------------------------+-----------------------------------------+
| Input | Parsed, and Output as %v |
+-------------------------------------------------------+-----------------------------------------+
| May 8, 2009 5:57:51 PM | 2009-05-08 17:57:51 +0000 UTC |
| oct 7, 1970 | 1970-10-07 00:00:00 +0000 UTC |
| oct 7, '70 | 1970-10-07 00:00:00 +0000 UTC |
| oct. 7, 1970 | 1970-10-07 00:00:00 +0000 UTC |
| oct. 7, 70 | 1970-10-07 00:00:00 +0000 UTC |
| Mon Jan 2 15:04:05 2006 | 2006-01-02 15:04:05 +0000 UTC |
| Mon Jan 2 15:04:05 MST 2006 | 2006-01-02 15:04:05 +0000 MST |
| Mon Jan 02 15:04:05 -0700 2006 | 2006-01-02 15:04:05 -0700 -0700 |
| Monday, 02-Jan-06 15:04:05 MST | 2006-01-02 15:04:05 +0000 MST |
| Mon, 02 Jan 2006 15:04:05 MST | 2006-01-02 15:04:05 +0000 MST |
| Tue, 11 Jul 2017 16:28:13 +0200 (CEST) | 2017-07-11 16:28:13 +0200 +0200 |
| Mon, 02 Jan 2006 15:04:05 -0700 | 2006-01-02 15:04:05 -0700 -0700 |
| Thu, 4 Jan 2018 17:53:36 +0000 | 2018-01-04 17:53:36 +0000 UTC |
| Mon Aug 10 15:44:11 UTC+0100 2015 | 2015-08-10 15:44:11 +0000 UTC |
| Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 GMT |
| September 17, 2012 10:09am | 2012-09-17 10:09:00 +0000 UTC |
| September 17, 2012 at 10:09am PST-08 | 2012-09-17 10:09:00 -0800 PST |
| September 17, 2012, 10:10:09 | 2012-09-17 10:10:09 +0000 UTC |
| October 7, 1970 | 1970-10-07 00:00:00 +0000 UTC |
| October 7th, 1970 | 1970-10-07 00:00:00 +0000 UTC |
| 12 Feb 2006, 19:17 | 2006-02-12 19:17:00 +0000 UTC |
| 12 Feb 2006 19:17 | 2006-02-12 19:17:00 +0000 UTC |
| 7 oct 70 | 1970-10-07 00:00:00 +0000 UTC |
| 7 oct 1970 | 1970-10-07 00:00:00 +0000 UTC |
| 03 February 2013 | 2013-02-03 00:00:00 +0000 UTC |
| 1 July 2013 | 2013-07-01 00:00:00 +0000 UTC |
| 2013-Feb-03 | 2013-02-03 00:00:00 +0000 UTC |
| 3/31/2014 | 2014-03-31 00:00:00 +0000 UTC |
| 03/31/2014 | 2014-03-31 00:00:00 +0000 UTC |
| 08/21/71 | 1971-08-21 00:00:00 +0000 UTC |
| 8/1/71 | 1971-08-01 00:00:00 +0000 UTC |
| 4/8/2014 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 04/08/2014 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 4/8/14 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 04/2/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC |
| 8/8/1965 12:00:00 AM | 1965-08-08 00:00:00 +0000 UTC |
| 8/8/1965 01:00:01 PM | 1965-08-08 13:00:01 +0000 UTC |
| 8/8/1965 01:00 PM | 1965-08-08 13:00:00 +0000 UTC |
| 8/8/1965 1:00 PM | 1965-08-08 13:00:00 +0000 UTC |
| 8/8/1965 12:00 AM | 1965-08-08 00:00:00 +0000 UTC |
| 4/02/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC |
| 03/19/2012 10:11:59 | 2012-03-19 10:11:59 +0000 UTC |
| 03/19/2012 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC |
| 2014/3/31 | 2014-03-31 00:00:00 +0000 UTC |
| 2014/03/31 | 2014-03-31 00:00:00 +0000 UTC |
| 2014/4/8 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 2014/04/08 22:05 | 2014-04-08 22:05:00 +0000 UTC |
| 2014/04/2 03:00:51 | 2014-04-02 03:00:51 +0000 UTC |
| 2014/4/02 03:00:51 | 2014-04-02 03:00:51 +0000 UTC |
| 2012/03/19 10:11:59 | 2012-03-19 10:11:59 +0000 UTC |
| 2012/03/19 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC |
| 2014年04月08日 | 2014-04-08 00:00:00 +0000 UTC |
| 2006-01-02T15:04:05+0000 | 2006-01-02 15:04:05 +0000 UTC |
| 2009-08-12T22:15:09-07:00 | 2009-08-12 22:15:09 -0700 -0700 |
| 2009-08-12T22:15:09 | 2009-08-12 22:15:09 +0000 UTC |
| 2009-08-12T22:15:09Z | 2009-08-12 22:15:09 +0000 UTC |
| 2014-04-26 17:24:37.3186369 | 2014-04-26 17:24:37.3186369 +0000 UTC |
| 2012-08-03 18:31:59.257000000 | 2012-08-03 18:31:59.257 +0000 UTC |
| 2014-04-26 17:24:37.123 | 2014-04-26 17:24:37.123 +0000 UTC |
| 2013-04-01 22:43 | 2013-04-01 22:43:00 +0000 UTC |
| 2013-04-01 22:43:22 | 2013-04-01 22:43:22 +0000 UTC |
| 2014-12-16 06:20:00 UTC | 2014-12-16 06:20:00 +0000 UTC |
| 2014-12-16 06:20:00 GMT | 2014-12-16 06:20:00 +0000 UTC |
| 2014-04-26 05:24:37 PM | 2014-04-26 17:24:37 +0000 UTC |
| 2014-04-26 13:13:43 +0800 | 2014-04-26 13:13:43 +0800 +0800 |
| 2014-04-26 13:13:43 +0800 +08 | 2014-04-26 13:13:43 +0800 +0800 |
| 2014-04-26 13:13:44 +09:00 | 2014-04-26 13:13:44 +0900 +0900 |
| 2012-08-03 18:31:59.257000000 +0000 UTC | 2012-08-03 18:31:59.257 +0000 UTC |
| 2015-09-30 18:48:56.35272715 +0000 UTC | 2015-09-30 18:48:56.35272715 +0000 UTC |
| 2015-02-18 00:12:00 +0000 GMT | 2015-02-18 00:12:00 +0000 UTC |
| 2015-02-18 00:12:00 +0000 UTC | 2015-02-18 00:12:00 +0000 UTC |
| 2015-02-08 03:02:00 +0300 MSK m=+0.000000001 | 2015-02-08 03:02:00 +0300 +0300 |
| 2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001 | 2015-02-08 03:02:00.001 +0300 +0300 |
| 2017-07-19 03:21:51+00:00 | 2017-07-19 03:21:51 +0000 UTC |
| 2014-04-26 | 2014-04-26 00:00:00 +0000 UTC |
| 2014-04 | 2014-04-01 00:00:00 +0000 UTC |
| 2014 | 2014-01-01 00:00:00 +0000 UTC |
| 2014-05-11 08:20:13,787 | 2014-05-11 08:20:13.787 +0000 UTC |
| 3.31.2014 | 2014-03-31 00:00:00 +0000 UTC |
| 03.31.2014 | 2014-03-31 00:00:00 +0000 UTC |
| 08.21.71 | 1971-08-21 00:00:00 +0000 UTC |
| 2014.03 | 2014-03-01 00:00:00 +0000 UTC |
| 2014.03.30 | 2014-03-30 00:00:00 +0000 UTC |
| 20140601 | 2014-06-01 00:00:00 +0000 UTC |
| 20140722105203 | 2014-07-22 10:52:03 +0000 UTC |
| 1332151919 | 2012-03-19 10:11:59 +0000 UTC |
| 1384216367189 | 2013-11-12 00:32:47.189 +0000 UTC |
| 1384216367111222 | 2013-11-12 00:32:47.111222 +0000 UTC |
| 1384216367111222333 | 2013-11-12 00:32:47.111222333 +0000 UTC |
+-------------------------------------------------------+-----------------------------------------+
*/
```

1864
vendor/github.com/araddon/dateparse/parseany.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

21
vendor/github.com/cpuguy83/go-md2man/v2/LICENSE.md generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Brian Goff
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,14 @@
package md2man
import (
"github.com/russross/blackfriday/v2"
)
// Render converts a markdown document into a roff formatted document.
func Render(doc []byte) []byte {
renderer := NewRoffRenderer()
return blackfriday.Run(doc,
[]blackfriday.Option{blackfriday.WithRenderer(renderer),
blackfriday.WithExtensions(renderer.GetExtensions())}...)
}

345
vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go generated vendored Normal file
View File

@ -0,0 +1,345 @@
package md2man
import (
"fmt"
"io"
"os"
"strings"
"github.com/russross/blackfriday/v2"
)
// roffRenderer implements the blackfriday.Renderer interface for creating
// roff format (manpages) from markdown text
type roffRenderer struct {
extensions blackfriday.Extensions
listCounters []int
firstHeader bool
defineTerm bool
listDepth int
}
const (
titleHeader = ".TH "
topLevelHeader = "\n\n.SH "
secondLevelHdr = "\n.SH "
otherHeader = "\n.SS "
crTag = "\n"
emphTag = "\\fI"
emphCloseTag = "\\fP"
strongTag = "\\fB"
strongCloseTag = "\\fP"
breakTag = "\n.br\n"
paraTag = "\n.PP\n"
hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n"
linkTag = "\n\\[la]"
linkCloseTag = "\\[ra]"
codespanTag = "\\fB\\fC"
codespanCloseTag = "\\fR"
codeTag = "\n.PP\n.RS\n\n.nf\n"
codeCloseTag = "\n.fi\n.RE\n"
quoteTag = "\n.PP\n.RS\n"
quoteCloseTag = "\n.RE\n"
listTag = "\n.RS\n"
listCloseTag = "\n.RE\n"
arglistTag = "\n.TP\n"
tableStart = "\n.TS\nallbox;\n"
tableEnd = ".TE\n"
tableCellStart = "T{\n"
tableCellEnd = "\nT}\n"
)
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
// from markdown
func NewRoffRenderer() *roffRenderer { // nolint: golint
var extensions blackfriday.Extensions
extensions |= blackfriday.NoIntraEmphasis
extensions |= blackfriday.Tables
extensions |= blackfriday.FencedCode
extensions |= blackfriday.SpaceHeadings
extensions |= blackfriday.Footnotes
extensions |= blackfriday.Titleblock
extensions |= blackfriday.DefinitionLists
return &roffRenderer{
extensions: extensions,
}
}
// GetExtensions returns the list of extensions used by this renderer implementation
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
return r.extensions
}
// RenderHeader handles outputting the header at document start
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
// disable hyphenation
out(w, ".nh\n")
}
// RenderFooter handles outputting the footer at the document end; the roff
// renderer has no footer information
func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
}
// RenderNode is called for each node in a markdown document; based on the node
// type the equivalent roff output is sent to the writer
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
var walkAction = blackfriday.GoToNext
switch node.Type {
case blackfriday.Text:
r.handleText(w, node, entering)
case blackfriday.Softbreak:
out(w, crTag)
case blackfriday.Hardbreak:
out(w, breakTag)
case blackfriday.Emph:
if entering {
out(w, emphTag)
} else {
out(w, emphCloseTag)
}
case blackfriday.Strong:
if entering {
out(w, strongTag)
} else {
out(w, strongCloseTag)
}
case blackfriday.Link:
if !entering {
out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag)
}
case blackfriday.Image:
// ignore images
walkAction = blackfriday.SkipChildren
case blackfriday.Code:
out(w, codespanTag)
escapeSpecialChars(w, node.Literal)
out(w, codespanCloseTag)
case blackfriday.Document:
break
case blackfriday.Paragraph:
// roff .PP markers break lists
if r.listDepth > 0 {
return blackfriday.GoToNext
}
if entering {
out(w, paraTag)
} else {
out(w, crTag)
}
case blackfriday.BlockQuote:
if entering {
out(w, quoteTag)
} else {
out(w, quoteCloseTag)
}
case blackfriday.Heading:
r.handleHeading(w, node, entering)
case blackfriday.HorizontalRule:
out(w, hruleTag)
case blackfriday.List:
r.handleList(w, node, entering)
case blackfriday.Item:
r.handleItem(w, node, entering)
case blackfriday.CodeBlock:
out(w, codeTag)
escapeSpecialChars(w, node.Literal)
out(w, codeCloseTag)
case blackfriday.Table:
r.handleTable(w, node, entering)
case blackfriday.TableCell:
r.handleTableCell(w, node, entering)
case blackfriday.TableHead:
case blackfriday.TableBody:
case blackfriday.TableRow:
// no action as cell entries do all the nroff formatting
return blackfriday.GoToNext
default:
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
}
return walkAction
}
func (r *roffRenderer) handleText(w io.Writer, node *blackfriday.Node, entering bool) {
var (
start, end string
)
// handle special roff table cell text encapsulation
if node.Parent.Type == blackfriday.TableCell {
if len(node.Literal) > 30 {
start = tableCellStart
end = tableCellEnd
} else {
// end rows that aren't terminated by "tableCellEnd" with a cr if end of row
if node.Parent.Next == nil && !node.Parent.IsHeader {
end = crTag
}
}
}
out(w, start)
escapeSpecialChars(w, node.Literal)
out(w, end)
}
func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
switch node.Level {
case 1:
if !r.firstHeader {
out(w, titleHeader)
r.firstHeader = true
break
}
out(w, topLevelHeader)
case 2:
out(w, secondLevelHdr)
default:
out(w, otherHeader)
}
}
}
func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
openTag := listTag
closeTag := listCloseTag
if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// tags for definition lists handled within Item node
openTag = ""
closeTag = ""
}
if entering {
r.listDepth++
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
r.listCounters = append(r.listCounters, 1)
}
out(w, openTag)
} else {
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
r.listCounters = r.listCounters[:len(r.listCounters)-1]
}
out(w, closeTag)
r.listDepth--
}
}
func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
r.listCounters[len(r.listCounters)-1]++
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// state machine for handling terms and following definitions
// since blackfriday does not distinguish them properly, nor
// does it seperate them into separate lists as it should
if !r.defineTerm {
out(w, arglistTag)
r.defineTerm = true
} else {
r.defineTerm = false
}
} else {
out(w, ".IP \\(bu 2\n")
}
} else {
out(w, "\n")
}
}
func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
out(w, tableStart)
//call walker to count cells (and rows?) so format section can be produced
columns := countColumns(node)
out(w, strings.Repeat("l ", columns)+"\n")
out(w, strings.Repeat("l ", columns)+".\n")
} else {
out(w, tableEnd)
}
}
func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
var (
start, end string
)
if node.IsHeader {
start = codespanTag
end = codespanCloseTag
}
if entering {
if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
out(w, "\t"+start)
} else {
out(w, start)
}
} else {
// need to carriage return if we are at the end of the header row
if node.IsHeader && node.Next == nil {
end = end + crTag
}
out(w, end)
}
}
// because roff format requires knowing the column count before outputting any table
// data we need to walk a table tree and count the columns
func countColumns(node *blackfriday.Node) int {
var columns int
node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
switch node.Type {
case blackfriday.TableRow:
if !entering {
return blackfriday.Terminate
}
case blackfriday.TableCell:
if entering {
columns++
}
default:
}
return blackfriday.GoToNext
})
return columns
}
func out(w io.Writer, output string) {
io.WriteString(w, output) // nolint: errcheck
}
func needsBackslash(c byte) bool {
for _, r := range []byte("-_&\\~") {
if c == r {
return true
}
}
return false
}
func escapeSpecialChars(w io.Writer, text []byte) {
for i := 0; i < len(text); i++ {
// escape initial apostrophe or period
if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {
out(w, "\\&")
}
// directly copy normal characters
org := i
for i < len(text) && !needsBackslash(text[i]) {
i++
}
if i > org {
w.Write(text[org:i]) // nolint: errcheck
}
// escape a character
if i >= len(text) {
break
}
w.Write([]byte{'\\', text[i]}) // nolint: errcheck
}
}

View File

@ -142,4 +142,4 @@
// - make error context accessible programmatically? // - make error context accessible programmatically?
// - limit input size? // - limit input size?
// //
package gcfg // import "github.com/src-d/gcfg" package gcfg // import "github.com/go-git/gcfg"

View File

@ -7,8 +7,8 @@ import (
"os" "os"
"strings" "strings"
"github.com/src-d/gcfg/scanner" "github.com/go-git/gcfg/scanner"
"github.com/src-d/gcfg/token" "github.com/go-git/gcfg/token"
"gopkg.in/warnings.v0" "gopkg.in/warnings.v0"
) )

View File

@ -11,7 +11,7 @@ import (
) )
import ( import (
"github.com/src-d/gcfg/token" "github.com/go-git/gcfg/token"
) )
// In an ErrorList, an error is represented by an *Error. // In an ErrorList, an error is represented by an *Error.

View File

@ -19,7 +19,7 @@ import (
) )
import ( import (
"github.com/src-d/gcfg/token" "github.com/go-git/gcfg/token"
) )
// An ErrorHandler may be provided to Scanner.Init. If a syntax error is // An ErrorHandler may be provided to Scanner.Init. If a syntax error is

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