mirror of
https://gitea.com/gitea/tea.git
synced 2025-09-19 02:02:55 +02:00
Compare commits
77 Commits
release/v0
...
v0.9.2
Author | SHA1 | Date | |
---|---|---|---|
fff1af1029 | |||
f28f199e85 | |||
c00418e74c | |||
6c9b2f8745 | |||
1a256291dc | |||
832136b6d4 | |||
99e49991bb | |||
bbb287e29e | |||
5e7c702e07 | |||
b8dbf899d2 | |||
0b8be54186 | |||
2b1bca9e5d | |||
d5a258213d | |||
f83f579dea | |||
65535bd948 | |||
02f5f15269 | |||
883a27b14e | |||
e54b32493d | |||
329200b1ef | |||
6663d9f19b | |||
d06f35482e | |||
9ab36c55fa | |||
40e606561f | |||
0970b94552 | |||
dda94a5dea | |||
16ba594a28 | |||
d8f4273ed0 | |||
637e3f0666 | |||
ced24ccabb | |||
fb3e1f75e9 | |||
0e24009fe9 | |||
cd24fd8e28 | |||
dd300c1269 | |||
44c9e7e664 | |||
268aa06179 | |||
a7d83ee416 | |||
a89f51f9ec | |||
d2295828d0 | |||
dc16643e0d | |||
ac25e89ebf | |||
819cc1ab21 | |||
78a95f1ca4 | |||
5b77345b03 | |||
23ce7b351d | |||
375ece06d2 | |||
4ffd994549 | |||
58aaa17e7e | |||
7a05be436c | |||
3cf084cb96 | |||
1e59dee685 | |||
42e423470c | |||
555f1ae516 | |||
1c690c5ff8 | |||
802bdf7dc5 | |||
1731e00ebd | |||
7b7c7f57be | |||
6e728cf812 | |||
808e8b1c5a | |||
9201250f74 | |||
5b28a05eb7 | |||
3fca309f2c | |||
d6df0a53b5 | |||
4b9907fb54 | |||
ab4e11ae4d | |||
546fcc16de | |||
0f4f669cf0 | |||
2bdd72dfff | |||
64770a771f | |||
ebb2c38a0a | |||
616127cedc | |||
3129e60a73 | |||
df724b4006 | |||
ffdbdb3d02 | |||
568fde1ce5 | |||
46945415c9 | |||
195bd2199c | |||
0bf844018c |
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Dockerfile
|
||||||
|
tea
|
84
.drone.yml
84
.drone.yml
@ -7,18 +7,25 @@ platform:
|
|||||||
arch: amd64
|
arch: amd64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: vendor
|
||||||
|
pull: always
|
||||||
|
image: golang:1.18
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
|
commands:
|
||||||
|
- make vendor # use vendor folder as cache
|
||||||
|
|
||||||
- name: build
|
- name: build
|
||||||
pull: always
|
pull: always
|
||||||
image: golang:1.16
|
image: golang:1.18
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
commands:
|
commands:
|
||||||
- make clean
|
- make clean
|
||||||
- make vet
|
- make vet
|
||||||
- make lint
|
- make lint
|
||||||
- make fmt-check
|
- make fmt-check
|
||||||
- make misspell-check
|
- make misspell-check
|
||||||
- make test-vendor
|
|
||||||
- make build
|
- make build
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
@ -27,24 +34,28 @@ steps:
|
|||||||
- pull_request
|
- pull_request
|
||||||
|
|
||||||
- name: unit-test
|
- name: unit-test
|
||||||
image: golang:1.16
|
image: golang:1.18
|
||||||
commands:
|
commands:
|
||||||
- make unit-test-coverage
|
- make unit-test-coverage
|
||||||
settings:
|
settings:
|
||||||
group: test
|
group: test
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
when:
|
when:
|
||||||
branch:
|
branch:
|
||||||
- master
|
- main
|
||||||
event:
|
event:
|
||||||
- push
|
- push
|
||||||
- pull_request
|
- pull_request
|
||||||
|
|
||||||
- name: release-test
|
- name: release-test
|
||||||
image: golang:1.16
|
image: golang:1.18
|
||||||
commands:
|
commands:
|
||||||
- make test
|
- make test
|
||||||
settings:
|
settings:
|
||||||
group: test
|
group: test
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
when:
|
when:
|
||||||
branch:
|
branch:
|
||||||
- "release/*"
|
- "release/*"
|
||||||
@ -54,21 +65,22 @@ steps:
|
|||||||
|
|
||||||
- name: tag-test
|
- name: tag-test
|
||||||
pull: always
|
pull: always
|
||||||
image: golang:1.16
|
image: golang:1.18
|
||||||
commands:
|
commands:
|
||||||
- make test
|
- make test
|
||||||
settings:
|
settings:
|
||||||
group: test
|
group: test
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
- tag
|
- tag
|
||||||
|
|
||||||
- name: static
|
- name: static
|
||||||
image: golang:1.16
|
image: golang:1.18
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
commands:
|
commands:
|
||||||
- export PATH=$PATH:$GOPATH/bin
|
|
||||||
- make release
|
- make release
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
@ -96,12 +108,18 @@ steps:
|
|||||||
|
|
||||||
- name: tag-release
|
- name: tag-release
|
||||||
pull: always
|
pull: always
|
||||||
image: plugins/s3:1
|
image: woodpeckerci/plugin-s3:latest
|
||||||
settings:
|
settings:
|
||||||
acl: public-read
|
acl:
|
||||||
bucket: releases
|
from_secret: aws_s3_acl
|
||||||
endpoint: https://storage.gitea.io
|
region:
|
||||||
path_style: true
|
from_secret: aws_s3_region
|
||||||
|
bucket:
|
||||||
|
from_secret: aws_s3_bucket
|
||||||
|
endpoint:
|
||||||
|
from_secret: aws_s3_endpoint
|
||||||
|
path_style:
|
||||||
|
from_secret: aws_s3_path_style
|
||||||
source: "dist/release/*"
|
source: "dist/release/*"
|
||||||
strip_prefix: dist/release/
|
strip_prefix: dist/release/
|
||||||
target: "/tea/${DRONE_TAG##v}"
|
target: "/tea/${DRONE_TAG##v}"
|
||||||
@ -116,12 +134,18 @@ steps:
|
|||||||
|
|
||||||
- name: release-branch-release
|
- name: release-branch-release
|
||||||
pull: always
|
pull: always
|
||||||
image: plugins/s3:1
|
image: woodpeckerci/plugin-s3:latest
|
||||||
settings:
|
settings:
|
||||||
acl: public-read
|
acl:
|
||||||
bucket: releases
|
from_secret: aws_s3_acl
|
||||||
endpoint: https://storage.gitea.io
|
region:
|
||||||
path_style: true
|
from_secret: aws_s3_region
|
||||||
|
bucket:
|
||||||
|
from_secret: aws_s3_bucket
|
||||||
|
endpoint:
|
||||||
|
from_secret: aws_s3_endpoint
|
||||||
|
path_style:
|
||||||
|
from_secret: aws_s3_path_style
|
||||||
source: "dist/release/*"
|
source: "dist/release/*"
|
||||||
strip_prefix: dist/release/
|
strip_prefix: dist/release/
|
||||||
target: "/tea/${DRONE_BRANCH##release/v}"
|
target: "/tea/${DRONE_BRANCH##release/v}"
|
||||||
@ -138,15 +162,21 @@ steps:
|
|||||||
|
|
||||||
- name: release
|
- name: release
|
||||||
pull: always
|
pull: always
|
||||||
image: plugins/s3:1
|
image: woodpeckerci/plugin-s3:latest
|
||||||
settings:
|
settings:
|
||||||
acl: public-read
|
acl:
|
||||||
bucket: releases
|
from_secret: aws_s3_acl
|
||||||
endpoint: https://storage.gitea.io
|
region:
|
||||||
path_style: true
|
from_secret: aws_s3_region
|
||||||
|
bucket:
|
||||||
|
from_secret: aws_s3_bucket
|
||||||
|
endpoint:
|
||||||
|
from_secret: aws_s3_endpoint
|
||||||
|
path_style:
|
||||||
|
from_secret: aws_s3_path_style
|
||||||
source: "dist/release/*"
|
source: "dist/release/*"
|
||||||
strip_prefix: dist/release/
|
strip_prefix: dist/release/
|
||||||
target: /tea/master
|
target: /tea/main
|
||||||
environment:
|
environment:
|
||||||
AWS_ACCESS_KEY_ID:
|
AWS_ACCESS_KEY_ID:
|
||||||
from_secret: aws_access_key_id
|
from_secret: aws_access_key_id
|
||||||
@ -154,7 +184,7 @@ steps:
|
|||||||
from_secret: aws_secret_access_key
|
from_secret: aws_secret_access_key
|
||||||
when:
|
when:
|
||||||
branch:
|
branch:
|
||||||
- master
|
- main
|
||||||
event:
|
event:
|
||||||
- push
|
- push
|
||||||
|
|
||||||
|
30
.gitea/issue_template/bug.md
Normal file
30
.gitea/issue_template/bug.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: "Bug Report"
|
||||||
|
about: "Use this template when reporting a bug, so you don't forget important information we'd ask for later."
|
||||||
|
title: "Bug: "
|
||||||
|
labels:
|
||||||
|
- kind/bug
|
||||||
|
---
|
||||||
|
|
||||||
|
### describe your environment
|
||||||
|
- tea version used (`tea -v`):
|
||||||
|
- [ ] I also reproduced the issue [with the latest master build](https://dl.gitea.io/tea/master)
|
||||||
|
- Gitea version used:
|
||||||
|
- [ ] the issue only occurred after updating gitea recently
|
||||||
|
- operating system:
|
||||||
|
- I make use of...
|
||||||
|
- [ ] non-standard default branch names (no `main`,`master`, or `trunk`)
|
||||||
|
- [ ] .ssh/config or .gitconfig host aliases in my git remotes
|
||||||
|
- [ ] ssh_agent or similar
|
||||||
|
- [ ] non-standard ports for gitea and/or ssh
|
||||||
|
- [ ] something else that's likely to interact badly with tea: ...
|
||||||
|
|
||||||
|
|
||||||
|
Please provide the output of `git remote -v` (if the issue is related to tea not finding resources on Gitea):
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### describe the issue (observed vs expected behaviour)
|
||||||
|
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ tea
|
|||||||
dist/
|
dist/
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
vendor/
|
||||||
|
@ -16,7 +16,6 @@ warningCode = 1
|
|||||||
[rule.increment-decrement]
|
[rule.increment-decrement]
|
||||||
[rule.var-naming]
|
[rule.var-naming]
|
||||||
[rule.var-declaration]
|
[rule.var-declaration]
|
||||||
[rule.package-comments]
|
|
||||||
[rule.range]
|
[rule.range]
|
||||||
[rule.receiver-naming]
|
[rule.receiver-naming]
|
||||||
[rule.time-naming]
|
[rule.time-naming]
|
||||||
|
73
CHANGELOG.md
73
CHANGELOG.md
@ -1,5 +1,78 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [v0.9.1](https://gitea.com/gitea/tea/releases/tag/v0.9.1) - 2023-02-15
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Print pull dont crash if it has TeamReviewRequests (#517)
|
||||||
|
|
||||||
|
## [v0.9.0](https://gitea.com/gitea/tea/releases/tag/v0.9.0) - 2022-09-13
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* Rename master branch to main (#495)
|
||||||
|
* Return RFC3339 UTC timestamps for machine-readable output (#470)
|
||||||
|
* FEATURES
|
||||||
|
* Allow editing multiline prompts with external text editor (#429)
|
||||||
|
* Add `tea admin user list` (#427)
|
||||||
|
* Add `tea whoami` command (#426)
|
||||||
|
* Add `tea org create <name>` (#420)
|
||||||
|
* Add `tea clone` (#411)
|
||||||
|
* Add `tea repo fork` (#410)
|
||||||
|
* Add `tea repo create-from-template` (#408)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fetch all items where needed. (#475)
|
||||||
|
* Fix running in repos without remote (#472)
|
||||||
|
* Add TSV to machine-readable formats (#467)
|
||||||
|
* Fix create milestone with deadline bug (#462)
|
||||||
|
* Fix resolving of URLs in markdown (#401)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Don't emit ANSI sequences when not emitting to TTY for markdown (#491)
|
||||||
|
* Show more version info (#486)
|
||||||
|
* Add preference `flag_defaults.remote`, refactor (#466)
|
||||||
|
* Add `--fields` to notification & milestone listings (#422)
|
||||||
|
* PR listing: add --fields & expose additional fields (#415)
|
||||||
|
* Add more flags to `tea repo create` (#409)
|
||||||
|
* Implement more issue filters (#400)
|
||||||
|
* MISC
|
||||||
|
* Simplify build & update installation instructions (#437)
|
||||||
|
* Clarify command descriptions when no arguments are taken (#496)
|
||||||
|
* Improve Documentation (#433)
|
||||||
|
* Use golang v1.18 and drop vendor folder (#478)
|
||||||
|
* Correct spelling of "wether" to "whether" in usage output (#453)
|
||||||
|
|
||||||
|
## [v0.8.0](https://gitea.com/gitea/tea/releases/tag/v0.8.0) - 2021-09-22
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* `tea notifications --all` has moved to `tea notifications --mine` (#389)
|
||||||
|
* `tea notifications` now only works with the context of a remote repo. (#389)
|
||||||
|
To run this outside of a local git dir, run either tea n `--mine` or `tea n --repo <my/repo>`
|
||||||
|
* FEATURES
|
||||||
|
* Add `tea pr merge` (#348)
|
||||||
|
* BUGFIXES
|
||||||
|
* Don't skip reading the local repo when `--repo` specifies a repo slug (#398)
|
||||||
|
* Fix adding login without token on private instances (#392)
|
||||||
|
* Correctly match login by ssh host with port (#391)
|
||||||
|
* Fix printing issue deadline (#388)
|
||||||
|
* Return useful error on wrong sshkey path (#374)
|
||||||
|
* Fix parsing of `--description` for issue/pr create (#371)
|
||||||
|
* Add missing flags (#369)
|
||||||
|
* Check negative limit command parameter (#358) (#359)
|
||||||
|
* Add missing flags to org & labels subcommands (#357)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Don't require a body for comment PR reviews (#399)
|
||||||
|
* Accept more main branch names for login detection (#396)
|
||||||
|
* Make local repo optional for `tea pr create`(#393)
|
||||||
|
* Notifications Add State Field (#384)
|
||||||
|
* Improve error messages (#370)
|
||||||
|
* Add tab completion for fish shell (#364)
|
||||||
|
* Text editor selection: follow unix defacto standards (#356)
|
||||||
|
* MISC
|
||||||
|
* Update Dependencies (#390)
|
||||||
|
|
||||||
|
## [v0.7.1](https://gitea.com/gitea/tea/releases/tag/v0.7.1) - 2021-08-27
|
||||||
|
|
||||||
|
* BUILD
|
||||||
|
* Enable release builds for darwin/arm64 (#360)
|
||||||
|
|
||||||
## [v0.7.0](https://gitea.com/gitea/tea/releases/tag/v0.7.0) - 2021-03-12
|
## [v0.7.0](https://gitea.com/gitea/tea/releases/tag/v0.7.0) - 2021-03-12
|
||||||
|
|
||||||
* BREAKING
|
* BREAKING
|
||||||
|
@ -57,8 +57,8 @@ high-level discussions.
|
|||||||
|
|
||||||
## Testing redux
|
## Testing redux
|
||||||
|
|
||||||
Before sending code out for review, run all the test by execting: `make test`
|
Before sending code out for review, run all the test by executing: `make test`
|
||||||
Since TEA is an cli tool it should be obvious to test your feature localy first.
|
Since TEA is an cli tool it should be obvious to test your feature locally first.
|
||||||
|
|
||||||
## Vendoring
|
## Vendoring
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ You can find more information on how to get started with it on the [dep project
|
|||||||
## Code review
|
## Code review
|
||||||
|
|
||||||
Changes to TEA must be reviewed before they are accepted—no matter who
|
Changes to TEA must be reviewed before they are accepted—no matter who
|
||||||
makes the change, even if they are an owner or a maintainer. We use Giteas's
|
makes the change, even if they are an owner or a maintainer. We use Gitea's
|
||||||
pull request & review workflow to do that. Gitea ensure every PR is reviewed by at least 2 maintainers.
|
pull request & review workflow to do that. Gitea ensure every PR is reviewed by at least 2 maintainers.
|
||||||
|
|
||||||
Please try to make your pull request easy to review for us. And, please read
|
Please try to make your pull request easy to review for us. And, please read
|
||||||
@ -118,8 +118,8 @@ Some of the key points:
|
|||||||
- Always make sure that the help texts are properly set, and as concise as possible.
|
- Always make sure that the help texts are properly set, and as concise as possible.
|
||||||
|
|
||||||
### Internal Module Structure
|
### Internal Module Structure
|
||||||
- `cmd`: only contains comand/flag options for `urfave/cli`
|
- `cmd`: only contains command/flag options for `urfave/cli`
|
||||||
- subcomands are in a subpackage named after its parent comand
|
- subcommands are in a subpackage named after its parent command
|
||||||
- `modules/task`: contain func for doing something with gitea
|
- `modules/task`: contain func for doing something with gitea
|
||||||
(e.g. create token by user/passwd)
|
(e.g. create token by user/passwd)
|
||||||
- `modules/print`: contain all functions that print to stdout
|
- `modules/print`: contain all functions that print to stdout
|
||||||
|
25
Dockerfile
Normal file
25
Dockerfile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
ARG GOVERSION="1.16.2"
|
||||||
|
|
||||||
|
FROM golang:${GOVERSION}-alpine AS buildenv
|
||||||
|
|
||||||
|
ARG GOOS="linux"
|
||||||
|
|
||||||
|
COPY . $GOPATH/src/
|
||||||
|
WORKDIR $GOPATH/src
|
||||||
|
|
||||||
|
RUN apk add --quiet --no-cache \
|
||||||
|
build-base \
|
||||||
|
make \
|
||||||
|
git && \
|
||||||
|
make clean build STATIC=true
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
ARG VERSION="0.7.0"
|
||||||
|
LABEL org.opencontainers.image.title="tea - CLI for Gitea - git with a cup of tea"
|
||||||
|
LABEL org.opencontainers.image.description="A command line tool to interact with Gitea servers"
|
||||||
|
LABEL org.opencontainers.image.version="${VERSION}"
|
||||||
|
LABEL org.opencontainers.image.authors="Tamás Gérczei <tamas@gerczei.eu>"
|
||||||
|
LABEL org.opencontainers.image.vendor="The Gitea Authors"
|
||||||
|
COPY --from=buildenv /go/src/tea /
|
||||||
|
ENV HOME="/app"
|
||||||
|
ENTRYPOINT ["/tea"]
|
101
Makefile
101
Makefile
@ -1,31 +1,15 @@
|
|||||||
DIST := dist
|
DIST := dist
|
||||||
IMPORT := code.gitea.io/tea
|
|
||||||
export GO111MODULE=on
|
export GO111MODULE=on
|
||||||
|
export CGO_ENABLED=0
|
||||||
|
|
||||||
GO ?= go
|
GO ?= go
|
||||||
SED_INPLACE := sed -i
|
|
||||||
SHASUM ?= shasum -a 256
|
SHASUM ?= shasum -a 256
|
||||||
|
|
||||||
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
|
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
|
||||||
|
|
||||||
ifeq ($(OS), Windows_NT)
|
|
||||||
EXECUTABLE := tea.exe
|
|
||||||
else
|
|
||||||
EXECUTABLE := tea
|
|
||||||
UNAME_S := $(shell uname -s)
|
|
||||||
ifeq ($(UNAME_S),Darwin)
|
|
||||||
SED_INPLACE := sed -i ''
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go")
|
GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go")
|
||||||
GOFMT ?= gofmt -s
|
GOFMT ?= gofmt -s
|
||||||
|
|
||||||
GOFLAGS := -i -v
|
|
||||||
EXTRA_GOFLAGS ?=
|
|
||||||
|
|
||||||
MAKE_VERSION := $(shell make -v | head -n 1)
|
|
||||||
|
|
||||||
ifneq ($(DRONE_TAG),)
|
ifneq ($(DRONE_TAG),)
|
||||||
VERSION ?= $(subst v,,$(DRONE_TAG))
|
VERSION ?= $(subst v,,$(DRONE_TAG))
|
||||||
TEA_VERSION ?= $(VERSION)
|
TEA_VERSION ?= $(VERSION)
|
||||||
@ -37,22 +21,27 @@ else
|
|||||||
endif
|
endif
|
||||||
TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
||||||
endif
|
endif
|
||||||
|
TEA_VERSION_TAG ?= $(shell sed 's/+/_/' <<< $(TEA_VERSION))
|
||||||
|
|
||||||
LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)"
|
TAGS ?=
|
||||||
|
SDK ?= $(shell $(GO) list -f '{{.Version}}' -m code.gitea.io/sdk/gitea)
|
||||||
|
LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)" -X "main.SDK=$(SDK)" -s -w
|
||||||
|
|
||||||
|
# override to allow passing additional goflags via make CLI
|
||||||
|
override GOFLAGS := $(GOFLAGS) -mod=vendor -tags '$(TAGS)' -ldflags '$(LDFLAGS)'
|
||||||
|
|
||||||
PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
|
PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
|
||||||
SOURCES ?= $(shell find . -name "*.go" -type f)
|
SOURCES ?= $(shell find . -name "*.go" -type f)
|
||||||
|
|
||||||
TAGS ?=
|
# OS specific vars.
|
||||||
|
|
||||||
ifeq ($(OS), Windows_NT)
|
ifeq ($(OS), Windows_NT)
|
||||||
EXECUTABLE := tea.exe
|
EXECUTABLE := tea.exe
|
||||||
else
|
else
|
||||||
EXECUTABLE := tea
|
EXECUTABLE := tea
|
||||||
|
ifneq ($(shell uname -s), OpenBSD)
|
||||||
|
override BUILDMODE := -buildmode=pie
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# $(call strip-suffix,filename)
|
|
||||||
strip-suffix = $(firstword $(subst ., ,$(1)))
|
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: build
|
all: build
|
||||||
@ -75,24 +64,15 @@ vet:
|
|||||||
$(GO) vet -vettool=gitea-vet $(PACKAGES)
|
$(GO) vet -vettool=gitea-vet $(PACKAGES)
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint: install-lint-tools
|
||||||
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
cd /tmp && $(GO) get -u github.com/mgechev/revive; \
|
|
||||||
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: install-lint-tools
|
||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
cd /tmp && $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
|
||||||
fi
|
|
||||||
misspell -error -i unknwon,destory $(GOFILES)
|
misspell -error -i unknwon,destory $(GOFILES)
|
||||||
|
|
||||||
.PHONY: misspell
|
.PHONY: misspell
|
||||||
misspell:
|
misspell: install-lint-tools
|
||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
cd /tmp && $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
|
||||||
fi
|
|
||||||
misspell -w -i unknwon $(GOFILES)
|
misspell -w -i unknwon $(GOFILES)
|
||||||
|
|
||||||
.PHONY: fmt-check
|
.PHONY: fmt-check
|
||||||
@ -117,30 +97,26 @@ unit-test-coverage:
|
|||||||
vendor:
|
vendor:
|
||||||
$(GO) mod tidy && $(GO) mod vendor
|
$(GO) mod tidy && $(GO) mod vendor
|
||||||
|
|
||||||
.PHONY: test-vendor
|
|
||||||
test-vendor: vendor
|
|
||||||
@diff=$$(git diff vendor/); \
|
|
||||||
if [ -n "$$diff" ]; then \
|
|
||||||
echo "Please run 'make vendor' and commit the result:"; \
|
|
||||||
echo "$${diff}"; \
|
|
||||||
exit 1; \
|
|
||||||
fi;
|
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: test
|
check: test
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install: $(wildcard *.go)
|
install: $(SOURCES)
|
||||||
$(GO) install -mod=vendor -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
@echo "installing to $(GOPATH)/bin/$(EXECUTABLE)"
|
||||||
|
$(GO) install -v $(BUILDMODE) $(GOFLAGS)
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: $(EXECUTABLE)
|
build: $(EXECUTABLE)
|
||||||
|
|
||||||
$(EXECUTABLE): $(SOURCES)
|
$(EXECUTABLE): $(SOURCES)
|
||||||
$(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
$(GO) build $(BUILDMODE) $(GOFLAGS) -o $@
|
||||||
|
|
||||||
|
.PHONY: build-image
|
||||||
|
build-image:
|
||||||
|
docker build --build-arg VERSION=$(TEA_VERSION) -t gitea/tea:$(TEA_VERSION_TAG) .
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: release-dirs release-os release-compress release-check
|
release: release-dirs install-release-tools release-os release-compress release-check
|
||||||
|
|
||||||
.PHONY: release-dirs
|
.PHONY: release-dirs
|
||||||
release-dirs:
|
release-dirs:
|
||||||
@ -148,18 +124,29 @@ release-dirs:
|
|||||||
|
|
||||||
.PHONY: release-os
|
.PHONY: release-os
|
||||||
release-os:
|
release-os:
|
||||||
@hash gox > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
CGO_ENABLED=0 gox -verbose -cgo=false $(GOFLAGS) -osarch='!darwin/386 !darwin/arm' -os="windows linux darwin" -arch="386 amd64 arm arm64" -output="$(DIST)/release/tea-$(VERSION)-{{.OS}}-{{.Arch}}"
|
||||||
cd /tmp && $(GO) get -u github.com/mitchellh/gox; \
|
|
||||||
fi
|
|
||||||
CGO_ENABLED=0 gox -verbose -cgo=false -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -osarch='!darwin/386 !darwin/arm64 !darwin/arm' -os="windows linux darwin" -arch="386 amd64 arm arm64" -output="$(DIST)/release/tea-$(VERSION)-{{.OS}}-{{.Arch}}"
|
|
||||||
|
|
||||||
.PHONY: release-compress
|
.PHONY: release-compress
|
||||||
release-compress:
|
release-compress: install-release-tools
|
||||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
|
||||||
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;
|
||||||
|
|
||||||
.PHONY: release-check
|
.PHONY: release-check
|
||||||
release-check:
|
release-check: install-release-tools
|
||||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
|
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
|
||||||
|
|
||||||
|
### tools
|
||||||
|
install-release-tools:
|
||||||
|
@hash gox > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
$(GO) install github.com/mitchellh/gox@latest; \
|
||||||
|
fi
|
||||||
|
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
$(GO) install github.com/ulikunitz/xz/cmd/gxz@latest; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
install-lint-tools:
|
||||||
|
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
$(GO) install github.com/mgechev/revive@latest; \
|
||||||
|
fi
|
||||||
|
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
$(GO) install github.com/client9/misspell/cmd/misspell@latest; \
|
||||||
|
fi
|
||||||
|
38
README.md
38
README.md
@ -8,16 +8,19 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
tea - command line tool to interact with Gitea
|
tea - command line tool to interact with Gitea
|
||||||
version 0.7.0-preview
|
version 0.8.0-preview
|
||||||
|
|
||||||
USAGE
|
USAGE
|
||||||
tea command [subcommand] [command options] [arguments...]
|
tea command [subcommand] [command options] [arguments...]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
tea is a productivity helper for Gitea. It can be used to manage most entities on one
|
tea is a productivity helper for Gitea. It can be used to manage most entities on
|
||||||
or multiple Gitea instances and provides local helpers like 'tea pull checkout'.
|
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.
|
||||||
tea makes use of context provided by the repository in $PWD if available, but is still
|
|
||||||
usable independently of $PWD. Configuration is persisted in $XDG_CONFIG_HOME/tea.
|
tea tries to make use of context provided by the repository in $PWD if available.
|
||||||
|
tea works best in a upstream/fork workflow, when the local main branch tracks the
|
||||||
|
upstream repo. tea assumes that local git state is published on the remote before
|
||||||
|
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.
|
||||||
|
|
||||||
COMMANDS
|
COMMANDS
|
||||||
help, h Shows a list of commands or help for one command
|
help, h Shows a list of commands or help for one command
|
||||||
@ -30,13 +33,16 @@
|
|||||||
times, time, t Operate on tracked times of a repository's issues & pulls
|
times, time, t Operate on tracked times of a repository's issues & pulls
|
||||||
organizations, organization, org List, create, delete organizations
|
organizations, organization, org List, create, delete organizations
|
||||||
repos, repo Show repository details
|
repos, repo Show repository details
|
||||||
|
comment, c Add a comment to an issue / pr
|
||||||
HELPERS:
|
HELPERS:
|
||||||
open, o Open something of the repository in web browser
|
open, o Open something of the repository in web browser
|
||||||
notifications, notification, n Show notifications
|
notifications, notification, n Show notifications
|
||||||
|
clone, C Clone a repository locally
|
||||||
SETUP:
|
SETUP:
|
||||||
logins, login Log in to a Gitea server
|
logins, login Log in to a Gitea server
|
||||||
logout Log out from a Gitea server
|
logout Log out from a Gitea server
|
||||||
shellcompletion, autocomplete Install shell completion for tea
|
shellcompletion, autocomplete Install shell completion for tea
|
||||||
|
whoami Show current logged in user
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
--help, -h show help (default: false)
|
--help, -h show help (default: false)
|
||||||
@ -58,7 +64,7 @@
|
|||||||
tea open milestones # open web ui for milestones
|
tea open milestones # open web ui for milestones
|
||||||
|
|
||||||
# send gitea desktop notifications every 5 minutes (bash + libnotify)
|
# send gitea desktop notifications every 5 minutes (bash + libnotify)
|
||||||
while :; do tea notifications --all -o simple | xargs -i notify-send {}; sleep 300; done
|
while :; do tea notifications --mine -o simple | xargs -i notify-send {}; sleep 300; done
|
||||||
|
|
||||||
ABOUT
|
ABOUT
|
||||||
Written & maintained by The Gitea Authors.
|
Written & maintained by The Gitea Authors.
|
||||||
@ -79,29 +85,31 @@ There are different ways to get `tea`:
|
|||||||
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
|
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
|
||||||
brew install tea
|
brew install tea
|
||||||
```
|
```
|
||||||
- arch linux ([gitea-tea](https://aur.archlinux.org/packages/gitea-tea), thirdparty)
|
- arch linux ([gitea-tea-git](https://aur.archlinux.org/packages/gitea-tea-git), thirdparty)
|
||||||
- alpine linux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge), thirdparty)
|
- alpine linux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge), thirdparty)
|
||||||
|
|
||||||
2. Use the prebuilt binaries from [dl.gitea.io](https://dl.gitea.io/tea/)
|
2. Use the prebuilt binaries from [dl.gitea.io](https://dl.gitea.io/tea/)
|
||||||
|
|
||||||
3. Install from source (go 1.13 or newer is required):
|
3. Install from source: [see *Compilation*](#compilation)
|
||||||
```sh
|
|
||||||
go get code.gitea.io/tea
|
|
||||||
go install code.gitea.io/tea
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Docker (thirdparty): [tgerczei/tea](https://hub.docker.com/r/tgerczei/tea)
|
4. Docker (thirdparty): [tgerczei/tea](https://hub.docker.com/r/tgerczei/tea)
|
||||||
|
|
||||||
## Compilation
|
## Compilation
|
||||||
|
|
||||||
Make sure you have installed a current go version.
|
Make sure you have a current go version installed (1.13 or newer).
|
||||||
To compile the sources yourself run the following:
|
|
||||||
|
|
||||||
|
- To compile the source yourself with the recommended flags & tags:
|
||||||
```sh
|
```sh
|
||||||
git clone https://gitea.com/gitea/tea.git
|
git clone https://gitea.com/gitea/tea.git # or: tea clone gitea.com/gitea/tea ;)
|
||||||
cd tea
|
cd tea
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
Note that GNU Make (gmake on OpenBSD) is required.
|
||||||
|
|
||||||
|
- For a quick installation without `git` & `make`:
|
||||||
|
```sh
|
||||||
|
go install code.gitea.io/tea@latest
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
1
build.go
1
build.go
@ -1,6 +1,7 @@
|
|||||||
// Copyright 2020 The Gitea 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.
|
||||||
|
//go:build vendor
|
||||||
// +build vendor
|
// +build vendor
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
55
cmd/admin.go
Normal file
55
cmd/admin.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/admin/users"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdAdmin represents the namespace of admin commands.
|
||||||
|
// The command itself has no functionality, but hosts subcommands.
|
||||||
|
var CmdAdmin = cli.Command{
|
||||||
|
Name: "admin",
|
||||||
|
Usage: "Operations requiring admin access on the Gitea instance",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Category: catMisc,
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
return cli.ShowSubcommandHelp(cmd)
|
||||||
|
},
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&cmdAdminUsers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdAdminUsers = cli.Command{
|
||||||
|
Name: "users",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Usage: "Manage registered users",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
if ctx.Args().Len() == 1 {
|
||||||
|
return runAdminUserDetail(ctx, ctx.Args().First())
|
||||||
|
}
|
||||||
|
return users.RunUserList(ctx)
|
||||||
|
},
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&users.CmdUserList,
|
||||||
|
},
|
||||||
|
Flags: users.CmdUserList.Flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAdminUserDetail(cmd *cli.Context, u string) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
user, _, err := client.GetUserInfo(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.UserDetails(user)
|
||||||
|
return nil
|
||||||
|
}
|
54
cmd/admin/users/list.go
Normal file
54
cmd/admin/users/list.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var userFieldsFlag = flags.FieldsFlag(print.UserFields, []string{
|
||||||
|
"id", "login", "full_name", "email", "activated",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdUserList represents a sub command of users to list users
|
||||||
|
var CmdUserList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List Users",
|
||||||
|
Description: "List users",
|
||||||
|
Action: RunUserList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
userFieldsFlag,
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunUserList list users
|
||||||
|
func RunUserList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
fields, err := userFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
users, _, err := client.AdminListUsers(gitea.AdminListUsersOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.UserList(users, ctx.Output, fields)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -22,7 +22,7 @@ var CmdAutocomplete = cli.Command{
|
|||||||
Category: catSetup,
|
Category: catSetup,
|
||||||
Usage: "Install shell completion for tea",
|
Usage: "Install shell completion for tea",
|
||||||
Description: "Install shell completion for tea",
|
Description: "Install shell completion for tea",
|
||||||
ArgsUsage: "<shell type> (bash, zsh, powershell)",
|
ArgsUsage: "<shell type> (bash, zsh, powershell, fish)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "install",
|
Name: "install",
|
||||||
@ -40,20 +40,26 @@ func runAutocompleteAdd(ctx *cli.Context) error {
|
|||||||
case "zsh":
|
case "zsh":
|
||||||
remoteFile = "contrib/autocomplete.zsh"
|
remoteFile = "contrib/autocomplete.zsh"
|
||||||
localFile = "autocomplete.zsh"
|
localFile = "autocomplete.zsh"
|
||||||
cmds = "echo 'PROG=tea _CLI_ZSH_AUTOCOMPLETE_HACK=1 source %s' >> ~/.zshrc && source ~/.zshrc"
|
cmds = "echo 'PROG=tea _CLI_ZSH_AUTOCOMPLETE_HACK=1 source \"%s\"' >> ~/.zshrc && source ~/.zshrc"
|
||||||
|
|
||||||
case "bash":
|
case "bash":
|
||||||
remoteFile = "contrib/autocomplete.sh"
|
remoteFile = "contrib/autocomplete.sh"
|
||||||
localFile = "autocomplete.sh"
|
localFile = "autocomplete.sh"
|
||||||
cmds = "echo 'PROG=tea source %s' >> ~/.bashrc && source ~/.bashrc"
|
cmds = "echo 'PROG=tea source \"%s\"' >> ~/.bashrc && source ~/.bashrc"
|
||||||
|
|
||||||
case "powershell":
|
case "powershell":
|
||||||
remoteFile = "contrib/autocomplete.ps1"
|
remoteFile = "contrib/autocomplete.ps1"
|
||||||
localFile = "tea.ps1"
|
localFile = "tea.ps1"
|
||||||
cmds = "\"& %s\" >> $profile"
|
cmds = "\"& %s\" >> $profile"
|
||||||
|
|
||||||
|
case "fish":
|
||||||
|
// fish is different, in that urfave/cli provides a generator for the shell script needed.
|
||||||
|
// this also means that the fish completion can become out of sync with the tea binary!
|
||||||
|
// writing to this directory suffices, as fish reads files there on startup, no cmds needed.
|
||||||
|
return writeFishAutoCompleteFile(ctx)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Must specify valid shell type")
|
return fmt.Errorf("Must specify valid %s", ctx.Command.ArgsUsage)
|
||||||
}
|
}
|
||||||
|
|
||||||
localPath, err := xdg.ConfigFile("tea/" + localFile)
|
localPath, err := xdg.ConfigFile("tea/" + localFile)
|
||||||
@ -62,8 +68,7 @@ func runAutocompleteAdd(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmds = fmt.Sprintf(cmds, localPath)
|
cmds = fmt.Sprintf(cmds, localPath)
|
||||||
|
if err = writeRemoteAutoCompleteFile(remoteFile, localPath); err != nil {
|
||||||
if err := saveAutoCompleteFile(remoteFile, localPath); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +90,7 @@ func runAutocompleteAdd(ctx *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveAutoCompleteFile(file, destPath string) error {
|
func writeRemoteAutoCompleteFile(file, destPath string) error {
|
||||||
url := fmt.Sprintf("https://gitea.com/gitea/tea/raw/branch/master/%s", file)
|
url := fmt.Sprintf("https://gitea.com/gitea/tea/raw/branch/master/%s", file)
|
||||||
fmt.Println("Fetching " + url)
|
fmt.Println("Fetching " + url)
|
||||||
|
|
||||||
@ -104,3 +109,30 @@ func saveAutoCompleteFile(file, destPath string) error {
|
|||||||
_, err = io.Copy(writer, res.Body)
|
_, err = io.Copy(writer, res.Body)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeFishAutoCompleteFile(ctx *cli.Context) error {
|
||||||
|
// NOTE: to make sure this file is in sync with tea commands, we'd need to
|
||||||
|
// - check if the file exists
|
||||||
|
// - if it does, check if the tea version that wrote it is the currently running version
|
||||||
|
// - if not, rewrite the file
|
||||||
|
// on each application run
|
||||||
|
// NOTE: this generates a completion that also suggests file names, which looks kinda messy..
|
||||||
|
script, err := ctx.App.ToFishCompletion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
localPath, err := xdg.ConfigFile("fish/conf.d/tea_completion.fish")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
writer, err := os.Create(localPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = io.WriteString(writer, script); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Installed tab completion to %s\n", localPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -8,4 +8,5 @@ var (
|
|||||||
catSetup = "SETUP"
|
catSetup = "SETUP"
|
||||||
catEntities = "ENTITIES"
|
catEntities = "ENTITIES"
|
||||||
catHelpers = "HELPERS"
|
catHelpers = "HELPERS"
|
||||||
|
catMisc = "MISCELLANEOUS"
|
||||||
)
|
)
|
||||||
|
89
cmd/clone.go
Normal file
89
cmd/clone.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/git"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoClone represents a sub command of repos to create a local copy
|
||||||
|
var CmdRepoClone = cli.Command{
|
||||||
|
Name: "clone",
|
||||||
|
Aliases: []string{"C"},
|
||||||
|
Usage: "Clone a repository locally",
|
||||||
|
Description: `Clone a repository locally, without a local git installation required.
|
||||||
|
The repo slug can be specified in different formats:
|
||||||
|
gitea/tea
|
||||||
|
tea
|
||||||
|
gitea.com/gitea/tea
|
||||||
|
git@gitea.com:gitea/tea
|
||||||
|
https://gitea.com/gitea/tea
|
||||||
|
ssh://gitea.com:22/gitea/tea
|
||||||
|
When a host is specified in the repo-slug, it will override the login specified with --login.
|
||||||
|
`,
|
||||||
|
Category: catHelpers,
|
||||||
|
Action: runRepoClone,
|
||||||
|
ArgsUsage: "<repo-slug> [target dir]",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "depth",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Usage: "num commits to fetch, defaults to all",
|
||||||
|
},
|
||||||
|
&flags.LoginFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoClone(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
args := ctx.Args()
|
||||||
|
if args.Len() < 1 {
|
||||||
|
return cli.ShowCommandHelp(cmd, "clone")
|
||||||
|
}
|
||||||
|
dir := args.Get(1)
|
||||||
|
|
||||||
|
var (
|
||||||
|
login *config.Login = ctx.Login
|
||||||
|
owner string = ctx.Login.User
|
||||||
|
repo string
|
||||||
|
)
|
||||||
|
|
||||||
|
// parse first arg as repo specifier
|
||||||
|
repoSlug := args.Get(0)
|
||||||
|
url, err := git.ParseURL(repoSlug)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
owner, repo = utils.GetOwnerAndRepo(url.Path, login.User)
|
||||||
|
if url.Host != "" {
|
||||||
|
login = config.GetLoginByHost(url.Host)
|
||||||
|
if login == nil {
|
||||||
|
return fmt.Errorf("No login configured matching host '%s', run `tea login add` first", url.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = task.RepoClone(
|
||||||
|
dir,
|
||||||
|
login,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
interact.PromptPassword,
|
||||||
|
ctx.Int("depth"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
@ -9,13 +9,15 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/tea/modules/interact"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
|
||||||
"code.gitea.io/tea/cmd/flags"
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
"code.gitea.io/tea/modules/print"
|
"code.gitea.io/tea/modules/print"
|
||||||
"code.gitea.io/tea/modules/utils"
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,7 +56,11 @@ func runAddComment(cmd *cli.Context) error {
|
|||||||
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
|
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
|
||||||
}
|
}
|
||||||
} else if len(body) == 0 {
|
} else if len(body) == 0 {
|
||||||
if body, err = interact.PromptMultiline("Content"); err != nil {
|
if err = survey.AskOne(interact.NewMultiline(interact.Multiline{
|
||||||
|
Message: "Comment:",
|
||||||
|
Syntax: "md",
|
||||||
|
UseEditor: config.GetPreferences().Editor,
|
||||||
|
}), &body); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
53
cmd/flags/csvflag.go
Normal file
53
cmd/flags/csvflag.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CsvFlag is a wrapper around cli.StringFlag, with an added GetValues() method
|
||||||
|
// to retrieve comma separated string values as a slice.
|
||||||
|
type CsvFlag struct {
|
||||||
|
cli.StringFlag
|
||||||
|
AvailableFields []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCsvFlag creates a CsvFlag, while setting its usage string and default values
|
||||||
|
func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag {
|
||||||
|
var availableDesc string
|
||||||
|
if len(availableValues) != 0 {
|
||||||
|
availableDesc = " Available values:"
|
||||||
|
}
|
||||||
|
return &CsvFlag{
|
||||||
|
AvailableFields: availableValues,
|
||||||
|
StringFlag: cli.StringFlag{
|
||||||
|
Name: name,
|
||||||
|
Aliases: aliases,
|
||||||
|
Value: strings.Join(defaults, ","),
|
||||||
|
Usage: fmt.Sprintf(`Comma-separated list of %s.%s
|
||||||
|
%s
|
||||||
|
`, usage, availableDesc, strings.Join(availableValues, ",")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValues returns the value of the flag, parsed as a commaseparated list
|
||||||
|
func (f CsvFlag) GetValues(ctx *cli.Context) ([]string, error) {
|
||||||
|
val := ctx.String(f.Name)
|
||||||
|
selection := strings.Split(val, ",")
|
||||||
|
if f.AvailableFields != nil && val != "" {
|
||||||
|
for _, field := range selection {
|
||||||
|
if !utils.Contains(f.AvailableFields, field) {
|
||||||
|
return nil, fmt.Errorf("Invalid field '%s'", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selection, nil
|
||||||
|
}
|
@ -1,205 +0,0 @@
|
|||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package flags
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
|
||||||
"code.gitea.io/tea/modules/context"
|
|
||||||
"code.gitea.io/tea/modules/task"
|
|
||||||
"code.gitea.io/tea/modules/utils"
|
|
||||||
|
|
||||||
"github.com/araddon/dateparse"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LoginFlag provides flag to specify tea login profile
|
|
||||||
var LoginFlag = cli.StringFlag{
|
|
||||||
Name: "login",
|
|
||||||
Aliases: []string{"l"},
|
|
||||||
Usage: "Use a different Gitea Login. Optional",
|
|
||||||
}
|
|
||||||
|
|
||||||
// RepoFlag provides flag to specify repository
|
|
||||||
var RepoFlag = cli.StringFlag{
|
|
||||||
Name: "repo",
|
|
||||||
Aliases: []string{"r"},
|
|
||||||
Usage: "Override local repository path or gitea repository slug to interact with. Optional",
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoteFlag provides flag to specify remote repository
|
|
||||||
var RemoteFlag = cli.StringFlag{
|
|
||||||
Name: "remote",
|
|
||||||
Aliases: []string{"R"},
|
|
||||||
Usage: "Discover Gitea login from remote. Optional",
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputFlag provides flag to specify output type
|
|
||||||
var OutputFlag = cli.StringFlag{
|
|
||||||
Name: "output",
|
|
||||||
Aliases: []string{"o"},
|
|
||||||
Usage: "Output format. (csv, simple, table, tsv, yaml)",
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateFlag provides flag to specify issue/pr state, defaulting to "open"
|
|
||||||
var StateFlag = cli.StringFlag{
|
|
||||||
Name: "state",
|
|
||||||
Usage: "Filter by state (all|open|closed)",
|
|
||||||
DefaultText: "open",
|
|
||||||
}
|
|
||||||
|
|
||||||
// PaginationPageFlag provides flag for pagination options
|
|
||||||
var PaginationPageFlag = cli.StringFlag{
|
|
||||||
Name: "page",
|
|
||||||
Aliases: []string{"p"},
|
|
||||||
Usage: "specify page, default is 1",
|
|
||||||
}
|
|
||||||
|
|
||||||
// PaginationLimitFlag provides flag for pagination options
|
|
||||||
var PaginationLimitFlag = cli.StringFlag{
|
|
||||||
Name: "limit",
|
|
||||||
Aliases: []string{"lm"},
|
|
||||||
Usage: "specify limit of items per page",
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoginOutputFlags defines login and output flags that should
|
|
||||||
// added to all subcommands and appended to the flags of the
|
|
||||||
// subcommand to work around issue and provide --login and --output:
|
|
||||||
// https://github.com/urfave/cli/issues/585
|
|
||||||
var LoginOutputFlags = []cli.Flag{
|
|
||||||
&LoginFlag,
|
|
||||||
&OutputFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoginRepoFlags defines login and repo flags that should
|
|
||||||
// be used for all subcommands and appended to the flags of
|
|
||||||
// the subcommand to work around issue and provide --login and --repo:
|
|
||||||
// https://github.com/urfave/cli/issues/585
|
|
||||||
var LoginRepoFlags = []cli.Flag{
|
|
||||||
&LoginFlag,
|
|
||||||
&RepoFlag,
|
|
||||||
&RemoteFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllDefaultFlags defines flags that should be available
|
|
||||||
// for all subcommands working with dedicated repositories
|
|
||||||
// to work around issue and provide --login, --repo and --output:
|
|
||||||
// https://github.com/urfave/cli/issues/585
|
|
||||||
var AllDefaultFlags = append([]cli.Flag{
|
|
||||||
&RepoFlag,
|
|
||||||
&RemoteFlag,
|
|
||||||
}, LoginOutputFlags...)
|
|
||||||
|
|
||||||
// IssuePRFlags defines flags that should be available on issue & pr listing flags.
|
|
||||||
var IssuePRFlags = append([]cli.Flag{
|
|
||||||
&StateFlag,
|
|
||||||
&PaginationPageFlag,
|
|
||||||
&PaginationLimitFlag,
|
|
||||||
}, AllDefaultFlags...)
|
|
||||||
|
|
||||||
// IssuePREditFlags defines flags for properties of issues and PRs
|
|
||||||
var IssuePREditFlags = append([]cli.Flag{
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "title",
|
|
||||||
Aliases: []string{"t"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "description",
|
|
||||||
Aliases: []string{"d"},
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "assignees",
|
|
||||||
Aliases: []string{"a"},
|
|
||||||
Usage: "Comma-separated list of usernames to assign",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "labels",
|
|
||||||
Aliases: []string{"L"},
|
|
||||||
Usage: "Comma-separated list of labels to assign",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "deadline",
|
|
||||||
Aliases: []string{"D"},
|
|
||||||
Usage: "Deadline timestamp to assign",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "milestone",
|
|
||||||
Aliases: []string{"m"},
|
|
||||||
Usage: "Milestone to assign",
|
|
||||||
},
|
|
||||||
}, LoginRepoFlags...)
|
|
||||||
|
|
||||||
// GetIssuePREditFlags parses all IssuePREditFlags
|
|
||||||
func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
|
|
||||||
opts := gitea.CreateIssueOption{
|
|
||||||
Title: ctx.String("title"),
|
|
||||||
Body: ctx.String("body"),
|
|
||||||
Assignees: strings.Split(ctx.String("assignees"), ","),
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
|
|
||||||
date := ctx.String("deadline")
|
|
||||||
if date != "" {
|
|
||||||
t, err := dateparse.ParseAny(date)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
opts.Deadline = &t
|
|
||||||
}
|
|
||||||
|
|
||||||
client := ctx.Login.Client()
|
|
||||||
|
|
||||||
labelNames := strings.Split(ctx.String("labels"), ",")
|
|
||||||
if len(labelNames) != 0 {
|
|
||||||
if client == nil {
|
|
||||||
client = ctx.Login.Client()
|
|
||||||
}
|
|
||||||
if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 {
|
|
||||||
if client == nil {
|
|
||||||
client = ctx.Login.Client()
|
|
||||||
}
|
|
||||||
ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Milestone '%s' not found", milestoneName)
|
|
||||||
}
|
|
||||||
opts.Milestone = ms.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
return &opts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FieldsFlag generates a flag selecting printable fields.
|
|
||||||
// To retrieve the value, use GetFields()
|
|
||||||
func FieldsFlag(availableFields, defaultFields []string) *cli.StringFlag {
|
|
||||||
return &cli.StringFlag{
|
|
||||||
Name: "fields",
|
|
||||||
Aliases: []string{"f"},
|
|
||||||
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
|
|
||||||
%s
|
|
||||||
`, strings.Join(availableFields, ",")),
|
|
||||||
Value: strings.Join(defaultFields, ","),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFields parses the values provided in a fields flag, and
|
|
||||||
// optionally validates against valid values.
|
|
||||||
func GetFields(ctx *cli.Context, validFields []string) ([]string, error) {
|
|
||||||
selection := strings.Split(ctx.String("fields"), ",")
|
|
||||||
if validFields != nil {
|
|
||||||
for _, field := range selection {
|
|
||||||
if !utils.Contains(validFields, field) {
|
|
||||||
return nil, fmt.Errorf("Invalid field '%s'", field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selection, nil
|
|
||||||
}
|
|
106
cmd/flags/generic.go
Normal file
106
cmd/flags/generic.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoginFlag provides flag to specify tea login profile
|
||||||
|
var LoginFlag = cli.StringFlag{
|
||||||
|
Name: "login",
|
||||||
|
Aliases: []string{"l"},
|
||||||
|
Usage: "Use a different Gitea Login. Optional",
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoFlag provides flag to specify repository
|
||||||
|
var RepoFlag = cli.StringFlag{
|
||||||
|
Name: "repo",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "Override local repository path or gitea repository slug to interact with. Optional",
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteFlag provides flag to specify remote repository
|
||||||
|
var RemoteFlag = cli.StringFlag{
|
||||||
|
Name: "remote",
|
||||||
|
Aliases: []string{"R"},
|
||||||
|
Usage: "Discover Gitea login from remote. Optional",
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutputFlag provides flag to specify output type
|
||||||
|
var OutputFlag = cli.StringFlag{
|
||||||
|
Name: "output",
|
||||||
|
Aliases: []string{"o"},
|
||||||
|
Usage: "Output format. (csv, simple, table, tsv, yaml)",
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaginationPageFlag provides flag for pagination options
|
||||||
|
var PaginationPageFlag = cli.StringFlag{
|
||||||
|
Name: "page",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "specify page, default is 1",
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaginationLimitFlag provides flag for pagination options
|
||||||
|
var PaginationLimitFlag = cli.StringFlag{
|
||||||
|
Name: "limit",
|
||||||
|
Aliases: []string{"lm"},
|
||||||
|
Usage: "specify limit of items per page",
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginOutputFlags defines login and output flags that should
|
||||||
|
// added to all subcommands and appended to the flags of the
|
||||||
|
// subcommand to work around issue and provide --login and --output:
|
||||||
|
// https://github.com/urfave/cli/issues/585
|
||||||
|
var LoginOutputFlags = []cli.Flag{
|
||||||
|
&LoginFlag,
|
||||||
|
&OutputFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginRepoFlags defines login and repo flags that should
|
||||||
|
// be used for all subcommands and appended to the flags of
|
||||||
|
// the subcommand to work around issue and provide --login and --repo:
|
||||||
|
// https://github.com/urfave/cli/issues/585
|
||||||
|
var LoginRepoFlags = []cli.Flag{
|
||||||
|
&LoginFlag,
|
||||||
|
&RepoFlag,
|
||||||
|
&RemoteFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllDefaultFlags defines flags that should be available
|
||||||
|
// for all subcommands working with dedicated repositories
|
||||||
|
// to work around issue and provide --login, --repo and --output:
|
||||||
|
// https://github.com/urfave/cli/issues/585
|
||||||
|
var AllDefaultFlags = append([]cli.Flag{
|
||||||
|
&RepoFlag,
|
||||||
|
&RemoteFlag,
|
||||||
|
}, LoginOutputFlags...)
|
||||||
|
|
||||||
|
// NotificationFlags defines flags that should be available on notifications.
|
||||||
|
var NotificationFlags = append([]cli.Flag{
|
||||||
|
NotificationStateFlag,
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "mine",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Show notifications across all your repositories instead of the current repository only",
|
||||||
|
},
|
||||||
|
&PaginationPageFlag,
|
||||||
|
&PaginationLimitFlag,
|
||||||
|
}, AllDefaultFlags...)
|
||||||
|
|
||||||
|
// NotificationStateFlag is a csv flag applied to all notification subcommands as filter
|
||||||
|
var NotificationStateFlag = NewCsvFlag(
|
||||||
|
"states",
|
||||||
|
"notification states to filter by",
|
||||||
|
[]string{"s"},
|
||||||
|
[]string{"pinned", "unread", "read"},
|
||||||
|
[]string{"unread", "pinned"},
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldsFlag generates a flag selecting printable fields.
|
||||||
|
// To retrieve the value, use f.GetValues()
|
||||||
|
func FieldsFlag(availableFields, defaultFields []string) *CsvFlag {
|
||||||
|
return NewCsvFlag("fields", "fields to print", []string{"f"}, availableFields, defaultFields)
|
||||||
|
}
|
161
cmd/flags/issue_pr.go
Normal file
161
cmd/flags/issue_pr.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"github.com/araddon/dateparse"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StateFlag provides flag to specify issue/pr state, defaulting to "open"
|
||||||
|
var StateFlag = cli.StringFlag{
|
||||||
|
Name: "state",
|
||||||
|
Usage: "Filter by state (all|open|closed)",
|
||||||
|
DefaultText: "open",
|
||||||
|
}
|
||||||
|
|
||||||
|
// MilestoneFilterFlag is a CSV flag used to filter issues by milestones
|
||||||
|
var MilestoneFilterFlag = NewCsvFlag(
|
||||||
|
"milestones",
|
||||||
|
"milestones to match issues against",
|
||||||
|
[]string{"m"}, nil, nil)
|
||||||
|
|
||||||
|
// LabelFilterFlag is a CSV flag used to filter issues by labels
|
||||||
|
var LabelFilterFlag = NewCsvFlag(
|
||||||
|
"labels",
|
||||||
|
"labels to match issues against",
|
||||||
|
[]string{"L"}, nil, nil)
|
||||||
|
|
||||||
|
// PRListingFlags defines flags that should be available on pr listing flags.
|
||||||
|
var PRListingFlags = append([]cli.Flag{
|
||||||
|
&StateFlag,
|
||||||
|
&PaginationPageFlag,
|
||||||
|
&PaginationLimitFlag,
|
||||||
|
}, AllDefaultFlags...)
|
||||||
|
|
||||||
|
// IssueListingFlags defines flags that should be available on issue listing flags.
|
||||||
|
var IssueListingFlags = append([]cli.Flag{
|
||||||
|
&StateFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "kind",
|
||||||
|
Aliases: []string{"K"},
|
||||||
|
Usage: "Whether to return `issues`, `pulls`, or `all` (you can use this to apply advanced search filters to PRs)",
|
||||||
|
DefaultText: "issues",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "keyword",
|
||||||
|
Aliases: []string{"k"},
|
||||||
|
Usage: "Filter by search string",
|
||||||
|
},
|
||||||
|
LabelFilterFlag,
|
||||||
|
MilestoneFilterFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "author",
|
||||||
|
Aliases: []string{"A"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "assignee",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "mentions",
|
||||||
|
Aliases: []string{"M"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "from",
|
||||||
|
Aliases: []string{"F"},
|
||||||
|
Usage: "Filter by activity after this date",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "until",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Usage: "Filter by activity before this date",
|
||||||
|
},
|
||||||
|
&PaginationPageFlag,
|
||||||
|
&PaginationLimitFlag,
|
||||||
|
}, AllDefaultFlags...)
|
||||||
|
|
||||||
|
// IssuePREditFlags defines flags for properties of issues and PRs
|
||||||
|
var IssuePREditFlags = append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "title",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "assignees",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "Comma-separated list of usernames to assign",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "labels",
|
||||||
|
Aliases: []string{"L"},
|
||||||
|
Usage: "Comma-separated list of labels to assign",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "deadline",
|
||||||
|
Aliases: []string{"D"},
|
||||||
|
Usage: "Deadline timestamp to assign",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "milestone",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Milestone to assign",
|
||||||
|
},
|
||||||
|
}, LoginRepoFlags...)
|
||||||
|
|
||||||
|
// GetIssuePREditFlags parses all IssuePREditFlags
|
||||||
|
func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
|
||||||
|
opts := gitea.CreateIssueOption{
|
||||||
|
Title: ctx.String("title"),
|
||||||
|
Body: ctx.String("description"),
|
||||||
|
Assignees: strings.Split(ctx.String("assignees"), ","),
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
date := ctx.String("deadline")
|
||||||
|
if date != "" {
|
||||||
|
t, err := dateparse.ParseAny(date)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.Deadline = &t
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
labelNames := strings.Split(ctx.String("labels"), ",")
|
||||||
|
if len(labelNames) != 0 {
|
||||||
|
if client == nil {
|
||||||
|
client = ctx.Login.Client()
|
||||||
|
}
|
||||||
|
if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 {
|
||||||
|
if client == nil {
|
||||||
|
client = ctx.Login.Client()
|
||||||
|
}
|
||||||
|
ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Milestone '%s' not found", milestoneName)
|
||||||
|
}
|
||||||
|
opts.Milestone = ms.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return &opts, nil
|
||||||
|
}
|
@ -34,7 +34,7 @@ var CmdIssues = cli.Command{
|
|||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "comments",
|
Name: "comments",
|
||||||
Usage: "Wether to display comments (will prompt if not provided & run interactively)",
|
Usage: "Whether to display comments (will prompt if not provided & run interactively)",
|
||||||
},
|
},
|
||||||
}, issues.CmdIssuesList.Flags...),
|
}, issues.CmdIssuesList.Flags...),
|
||||||
}
|
}
|
||||||
@ -54,11 +54,16 @@ func runIssueDetail(cmd *cli.Context, index string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
issue, _, err := ctx.Login.Client().GetIssue(ctx.Owner, ctx.Repo, idx)
|
client := ctx.Login.Client()
|
||||||
|
issue, _, err := client.GetIssue(ctx.Owner, ctx.Repo, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
print.IssueDetails(issue)
|
reactions, _, err := client.GetIssueReactions(ctx.Owner, ctx.Repo, idx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.IssueDetails(issue, reactions)
|
||||||
|
|
||||||
if issue.Comments > 0 {
|
if issue.Comments > 0 {
|
||||||
err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments)
|
err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments)
|
||||||
|
@ -47,6 +47,6 @@ func editIssueState(cmd *cli.Context, opts gitea.EditIssueOption) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
print.IssueDetails(issue)
|
print.IssueDetails(issue, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ var CmdIssuesCreate = cli.Command{
|
|||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
Usage: "Create an issue on repository",
|
Usage: "Create an issue on repository",
|
||||||
Description: `Create an issue on repository`,
|
Description: `Create an issue on repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runIssuesCreate,
|
Action: runIssuesCreate,
|
||||||
Flags: flags.IssuePREditFlags,
|
Flags: flags.IssuePREditFlags,
|
||||||
}
|
}
|
||||||
|
@ -5,26 +5,31 @@
|
|||||||
package issues
|
package issues
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/tea/cmd/flags"
|
"code.gitea.io/tea/cmd/flags"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
"code.gitea.io/tea/modules/print"
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/araddon/dateparse"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var issueFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{
|
||||||
|
"index", "title", "state", "author", "milestone", "labels",
|
||||||
|
})
|
||||||
|
|
||||||
// CmdIssuesList represents a sub command of issues to list issues
|
// CmdIssuesList represents a sub command of issues to list issues
|
||||||
var CmdIssuesList = cli.Command{
|
var CmdIssuesList = cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
Usage: "List issues of the repository",
|
Usage: "List issues of the repository",
|
||||||
Description: `List issues of the repository`,
|
Description: `List issues of the repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: RunIssuesList,
|
Action: RunIssuesList,
|
||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssueListingFlags...),
|
||||||
flags.FieldsFlag(print.IssueFields, []string{
|
|
||||||
"index", "title", "state", "author", "milestone", "labels",
|
|
||||||
}),
|
|
||||||
}, flags.IssuePRFlags...),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunIssuesList list issues
|
// RunIssuesList list issues
|
||||||
@ -36,23 +41,64 @@ func RunIssuesList(cmd *cli.Context) error {
|
|||||||
switch ctx.String("state") {
|
switch ctx.String("state") {
|
||||||
case "all":
|
case "all":
|
||||||
state = gitea.StateAll
|
state = gitea.StateAll
|
||||||
case "open":
|
case "", "open":
|
||||||
state = gitea.StateOpen
|
state = gitea.StateOpen
|
||||||
case "closed":
|
case "closed":
|
||||||
state = gitea.StateClosed
|
state = gitea.StateClosed
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown state '%s'", ctx.String("state"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kind := gitea.IssueTypeIssue
|
||||||
|
switch ctx.String("kind") {
|
||||||
|
case "", "issues", "issue":
|
||||||
|
kind = gitea.IssueTypeIssue
|
||||||
|
case "pulls", "pull", "pr":
|
||||||
|
kind = gitea.IssueTypePull
|
||||||
|
case "all":
|
||||||
|
kind = gitea.IssueTypeAll
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown kind '%s'", ctx.String("kind"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var from, until time.Time
|
||||||
|
if ctx.IsSet("from") {
|
||||||
|
from, err = dateparse.ParseLocal(ctx.String("from"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ctx.IsSet("until") {
|
||||||
|
until, err = dateparse.ParseLocal(ctx.String("until"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore error, as we don't do any input validation on these flags
|
||||||
|
labels, _ := flags.LabelFilterFlag.GetValues(cmd)
|
||||||
|
milestones, _ := flags.MilestoneFilterFlag.GetValues(cmd)
|
||||||
|
|
||||||
issues, _, err := ctx.Login.Client().ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{
|
issues, _, err := ctx.Login.Client().ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{
|
||||||
ListOptions: ctx.GetListOptions(),
|
ListOptions: ctx.GetListOptions(),
|
||||||
State: state,
|
State: state,
|
||||||
Type: gitea.IssueTypeIssue,
|
Type: kind,
|
||||||
|
KeyWord: ctx.String("keyword"),
|
||||||
|
CreatedBy: ctx.String("author"),
|
||||||
|
AssignedBy: ctx.String("assigned-to"),
|
||||||
|
MentionedBy: ctx.String("mentions"),
|
||||||
|
Labels: labels,
|
||||||
|
Milestones: milestones,
|
||||||
|
Since: from,
|
||||||
|
Before: until,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fields, err := flags.GetFields(cmd, print.IssueFields)
|
fields, err := issueFieldsFlag.GetValues(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ var CmdLabels = cli.Command{
|
|||||||
Category: catEntities,
|
Category: catEntities,
|
||||||
Usage: "Manage issue labels",
|
Usage: "Manage issue labels",
|
||||||
Description: `Manage issue labels`,
|
Description: `Manage issue labels`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runLabels,
|
Action: runLabels,
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
&labels.CmdLabelsList,
|
&labels.CmdLabelsList,
|
||||||
@ -25,6 +26,7 @@ var CmdLabels = cli.Command{
|
|||||||
&labels.CmdLabelUpdate,
|
&labels.CmdLabelUpdate,
|
||||||
&labels.CmdLabelDelete,
|
&labels.CmdLabelDelete,
|
||||||
},
|
},
|
||||||
|
Flags: labels.CmdLabelsList.Flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLabels(ctx *cli.Context) error {
|
func runLabels(ctx *cli.Context) error {
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
@ -22,8 +23,9 @@ var CmdLabelCreate = cli.Command{
|
|||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
Usage: "Create a label",
|
Usage: "Create a label",
|
||||||
Description: `Create a label`,
|
Description: `Create a label`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runLabelCreate,
|
Action: runLabelCreate,
|
||||||
Flags: []cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Usage: "label name",
|
Usage: "label name",
|
||||||
@ -40,7 +42,7 @@ var CmdLabelCreate = cli.Command{
|
|||||||
Name: "file",
|
Name: "file",
|
||||||
Usage: "indicate a label file",
|
Usage: "indicate a label file",
|
||||||
},
|
},
|
||||||
},
|
}, flags.AllDefaultFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLabelCreate(cmd *cli.Context) error {
|
func runLabelCreate(cmd *cli.Context) error {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package labels
|
package labels
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@ -16,13 +17,14 @@ var CmdLabelDelete = cli.Command{
|
|||||||
Aliases: []string{"rm"},
|
Aliases: []string{"rm"},
|
||||||
Usage: "Delete a label",
|
Usage: "Delete a label",
|
||||||
Description: `Delete a label`,
|
Description: `Delete a label`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runLabelDelete,
|
Action: runLabelDelete,
|
||||||
Flags: []cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Usage: "label id",
|
Usage: "label id",
|
||||||
},
|
},
|
||||||
},
|
}, flags.AllDefaultFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLabelDelete(cmd *cli.Context) error {
|
func runLabelDelete(cmd *cli.Context) error {
|
||||||
|
@ -20,6 +20,7 @@ var CmdLabelsList = cli.Command{
|
|||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
Usage: "List labels",
|
Usage: "List labels",
|
||||||
Description: "List labels",
|
Description: "List labels",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: RunLabelsList,
|
Action: RunLabelsList,
|
||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package labels
|
package labels
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
@ -16,8 +17,9 @@ var CmdLabelUpdate = cli.Command{
|
|||||||
Name: "update",
|
Name: "update",
|
||||||
Usage: "Update a label",
|
Usage: "Update a label",
|
||||||
Description: `Update a label`,
|
Description: `Update a label`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runLabelUpdate,
|
Action: runLabelUpdate,
|
||||||
Flags: []cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Usage: "label id",
|
Usage: "label id",
|
||||||
@ -34,7 +36,7 @@ var CmdLabelUpdate = cli.Command{
|
|||||||
Name: "description",
|
Name: "description",
|
||||||
Usage: "label description",
|
Usage: "label description",
|
||||||
},
|
},
|
||||||
},
|
}, flags.AllDefaultFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLabelUpdate(cmd *cli.Context) error {
|
func runLabelUpdate(cmd *cli.Context) error {
|
||||||
|
@ -16,6 +16,7 @@ var CmdLoginAdd = cli.Command{
|
|||||||
Name: "add",
|
Name: "add",
|
||||||
Usage: "Add a Gitea login",
|
Usage: "Add a Gitea login",
|
||||||
Description: `Add a Gitea login, without args it will create one interactively`,
|
Description: `Add a Gitea login, without args it will create one interactively`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
|
@ -18,6 +18,7 @@ var CmdLoginEdit = cli.Command{
|
|||||||
Aliases: []string{"e"},
|
Aliases: []string{"e"},
|
||||||
Usage: "Edit Gitea logins",
|
Usage: "Edit Gitea logins",
|
||||||
Description: `Edit Gitea logins`,
|
Description: `Edit Gitea logins`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runLoginEdit,
|
Action: runLoginEdit,
|
||||||
Flags: []cli.Flag{&flags.OutputFlag},
|
Flags: []cli.Flag{&flags.OutputFlag},
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ var CmdLoginList = cli.Command{
|
|||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
Usage: "List Gitea logins",
|
Usage: "List Gitea logins",
|
||||||
Description: `List Gitea logins`,
|
Description: `List Gitea logins`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: RunLoginList,
|
Action: RunLoginList,
|
||||||
Flags: []cli.Flag{&flags.OutputFlag},
|
Flags: []cli.Flag{&flags.OutputFlag},
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ var CmdMilestonesCreate = cli.Command{
|
|||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
Usage: "Create an milestone on repository",
|
Usage: "Create an milestone on repository",
|
||||||
Description: `Create an milestone on repository`,
|
Description: `Create an milestone on repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runMilestonesCreate,
|
Action: runMilestonesCreate,
|
||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
@ -55,7 +56,7 @@ func runMilestonesCreate(cmd *cli.Context) error {
|
|||||||
deadline := &time.Time{}
|
deadline := &time.Time{}
|
||||||
if date != "" {
|
if date != "" {
|
||||||
t, err := dateparse.ParseAny(date)
|
t, err := dateparse.ParseAny(date)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
deadline = &t
|
deadline = &t
|
||||||
|
@ -16,6 +16,10 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var msIssuesFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{
|
||||||
|
"index", "kind", "title", "state", "updated", "labels",
|
||||||
|
})
|
||||||
|
|
||||||
// CmdMilestonesIssues represents a sub command of milestones to manage issue/pull of an milestone
|
// CmdMilestonesIssues represents a sub command of milestones to manage issue/pull of an milestone
|
||||||
var CmdMilestonesIssues = cli.Command{
|
var CmdMilestonesIssues = cli.Command{
|
||||||
Name: "issues",
|
Name: "issues",
|
||||||
@ -40,9 +44,7 @@ var CmdMilestonesIssues = cli.Command{
|
|||||||
},
|
},
|
||||||
&flags.PaginationPageFlag,
|
&flags.PaginationPageFlag,
|
||||||
&flags.PaginationLimitFlag,
|
&flags.PaginationLimitFlag,
|
||||||
flags.FieldsFlag(print.IssueFields, []string{
|
msIssuesFieldsFlag,
|
||||||
"index", "kind", "title", "state", "updated", "labels",
|
|
||||||
}),
|
|
||||||
}, flags.AllDefaultFlags...),
|
}, flags.AllDefaultFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +112,7 @@ func runMilestoneIssueList(cmd *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fields, err := flags.GetFields(cmd, print.IssueFields)
|
fields, err := msIssuesFieldsFlag.GetValues(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,20 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var fieldsFlag = flags.FieldsFlag(print.MilestoneFields, []string{
|
||||||
|
"title", "items", "duedate",
|
||||||
|
})
|
||||||
|
|
||||||
// CmdMilestonesList represents a sub command of milestones to list milestones
|
// CmdMilestonesList represents a sub command of milestones to list milestones
|
||||||
var CmdMilestonesList = cli.Command{
|
var CmdMilestonesList = cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
Usage: "List milestones of the repository",
|
Usage: "List milestones of the repository",
|
||||||
Description: `List milestones of the repository`,
|
Description: `List milestones of the repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: RunMilestonesList,
|
Action: RunMilestonesList,
|
||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
|
fieldsFlag,
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "state",
|
Name: "state",
|
||||||
Usage: "Filter by milestone state (all|open|closed)",
|
Usage: "Filter by milestone state (all|open|closed)",
|
||||||
@ -36,10 +42,18 @@ func RunMilestonesList(cmd *cli.Context) error {
|
|||||||
ctx := context.InitCommand(cmd)
|
ctx := context.InitCommand(cmd)
|
||||||
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
fields, err := fieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
state := gitea.StateOpen
|
state := gitea.StateOpen
|
||||||
switch ctx.String("state") {
|
switch ctx.String("state") {
|
||||||
case "all":
|
case "all":
|
||||||
state = gitea.StateAll
|
state = gitea.StateAll
|
||||||
|
if !cmd.IsSet("fields") { // add to default fields
|
||||||
|
fields = append(fields, "state")
|
||||||
|
}
|
||||||
case "closed":
|
case "closed":
|
||||||
state = gitea.StateClosed
|
state = gitea.StateClosed
|
||||||
}
|
}
|
||||||
@ -54,6 +68,6 @@ func RunMilestonesList(cmd *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
print.MilestonesList(milestones, ctx.Output, state)
|
print.MilestonesList(milestones, ctx.Output, fields)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,8 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/tea/cmd/flags"
|
"code.gitea.io/tea/cmd/notifications"
|
||||||
"code.gitea.io/tea/modules/context"
|
|
||||||
"code.gitea.io/tea/modules/print"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,65 +16,14 @@ var CmdNotifications = cli.Command{
|
|||||||
Aliases: []string{"notification", "n"},
|
Aliases: []string{"notification", "n"},
|
||||||
Category: catHelpers,
|
Category: catHelpers,
|
||||||
Usage: "Show notifications",
|
Usage: "Show notifications",
|
||||||
Description: "Show notifications, by default based of the current repo and unread one",
|
Description: "Show notifications, by default based on the current repo if available",
|
||||||
Action: runNotifications,
|
Action: notifications.RunNotificationsList,
|
||||||
Flags: append([]cli.Flag{
|
Subcommands: []*cli.Command{
|
||||||
&cli.BoolFlag{
|
¬ifications.CmdNotificationsList,
|
||||||
Name: "all",
|
¬ifications.CmdNotificationsMarkRead,
|
||||||
Aliases: []string{"a"},
|
¬ifications.CmdNotificationsMarkUnread,
|
||||||
Usage: "show all notifications of related gitea instance",
|
¬ifications.CmdNotificationsMarkPinned,
|
||||||
|
¬ifications.CmdNotificationsUnpin,
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
Flags: notifications.CmdNotificationsList.Flags,
|
||||||
Name: "read",
|
|
||||||
Aliases: []string{"rd"},
|
|
||||||
Usage: "show read notifications instead unread",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "pinned",
|
|
||||||
Aliases: []string{"pd"},
|
|
||||||
Usage: "show pinned notifications instead unread",
|
|
||||||
},
|
|
||||||
&flags.PaginationPageFlag,
|
|
||||||
&flags.PaginationLimitFlag,
|
|
||||||
}, flags.AllDefaultFlags...),
|
|
||||||
}
|
|
||||||
|
|
||||||
func runNotifications(cmd *cli.Context) error {
|
|
||||||
var news []*gitea.NotificationThread
|
|
||||||
var err error
|
|
||||||
|
|
||||||
ctx := context.InitCommand(cmd)
|
|
||||||
client := ctx.Login.Client()
|
|
||||||
|
|
||||||
listOpts := ctx.GetListOptions()
|
|
||||||
if listOpts.Page == 0 {
|
|
||||||
listOpts.Page = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var status []gitea.NotifyStatus
|
|
||||||
if ctx.Bool("read") {
|
|
||||||
status = []gitea.NotifyStatus{gitea.NotifyStatusRead}
|
|
||||||
}
|
|
||||||
if ctx.Bool("pinned") {
|
|
||||||
status = append(status, gitea.NotifyStatusPinned)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Bool("all") {
|
|
||||||
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
|
|
||||||
ListOptions: listOpts,
|
|
||||||
Status: status,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
|
||||||
news, _, err = client.ListRepoNotifications(ctx.Owner, ctx.Repo, gitea.ListNotificationOptions{
|
|
||||||
ListOptions: listOpts,
|
|
||||||
Status: status,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
print.NotificationsList(news, ctx.Output, ctx.Bool("all"))
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
107
cmd/notifications/list.go
Normal file
107
cmd/notifications/list.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package notifications
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var notifyFieldsFlag = flags.FieldsFlag(print.NotificationFields, []string{
|
||||||
|
"id", "status", "index", "type", "state", "title",
|
||||||
|
})
|
||||||
|
|
||||||
|
var notifyTypeFlag = flags.NewCsvFlag("types", "subject types to filter by", []string{"t"},
|
||||||
|
[]string{"issue", "pull", "repository", "commit"}, nil)
|
||||||
|
|
||||||
|
// CmdNotificationsList represents a sub command of notifications to list notifications
|
||||||
|
var CmdNotificationsList = cli.Command{
|
||||||
|
Name: "ls",
|
||||||
|
Aliases: []string{"list"},
|
||||||
|
Usage: "List notifications",
|
||||||
|
Description: `List notifications`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunNotificationsList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
notifyFieldsFlag,
|
||||||
|
notifyTypeFlag,
|
||||||
|
}, flags.NotificationFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunNotificationsList list notifications
|
||||||
|
func RunNotificationsList(ctx *cli.Context) error {
|
||||||
|
var states []gitea.NotifyStatus
|
||||||
|
statesStr, err := flags.NotificationStateFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, s := range statesStr {
|
||||||
|
states = append(states, gitea.NotifyStatus(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
var types []gitea.NotifySubjectType
|
||||||
|
typesStr, err := notifyTypeFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, t := range typesStr {
|
||||||
|
types = append(types, gitea.NotifySubjectType(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
return listNotifications(ctx, states, types)
|
||||||
|
}
|
||||||
|
|
||||||
|
// listNotifications will get the notifications based on status and subject type
|
||||||
|
func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects []gitea.NotifySubjectType) error {
|
||||||
|
var news []*gitea.NotificationThread
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
all := ctx.Bool("mine")
|
||||||
|
|
||||||
|
// This enforces pagination (see https://github.com/go-gitea/gitea/issues/16733)
|
||||||
|
listOpts := ctx.GetListOptions()
|
||||||
|
if listOpts.Page == 0 {
|
||||||
|
listOpts.Page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := notifyFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if all {
|
||||||
|
// add repository to the default fields
|
||||||
|
if !cmd.IsSet("fields") {
|
||||||
|
fields = append(fields, "repository")
|
||||||
|
}
|
||||||
|
|
||||||
|
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
|
||||||
|
ListOptions: listOpts,
|
||||||
|
Status: status,
|
||||||
|
SubjectTypes: subjects,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
news, _, err = client.ListRepoNotifications(ctx.Owner, ctx.Repo, gitea.ListNotificationOptions{
|
||||||
|
ListOptions: listOpts,
|
||||||
|
Status: status,
|
||||||
|
SubjectTypes: subjects,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
print.NotificationsList(news, ctx.Output, fields)
|
||||||
|
return nil
|
||||||
|
}
|
139
cmd/notifications/mark_as.go
Normal file
139
cmd/notifications/mark_as.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package notifications
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdNotificationsMarkRead represents a sub command of notifications to list read notifications
|
||||||
|
var CmdNotificationsMarkRead = cli.Command{
|
||||||
|
Name: "read",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "Mark all filtered or a specific notification as read",
|
||||||
|
Description: "Mark all filtered or a specific notification as read",
|
||||||
|
ArgsUsage: "[all | <notification id>]",
|
||||||
|
Flags: flags.NotificationFlags,
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
cmd := context.InitCommand(ctx)
|
||||||
|
filter, err := flags.NotificationStateFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !cmd.IsSet(flags.NotificationStateFlag.Name) {
|
||||||
|
filter = []string{string(gitea.NotifyStatusUnread)}
|
||||||
|
}
|
||||||
|
return markNotificationAs(cmd, filter, gitea.NotifyStatusRead)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdNotificationsMarkUnread will mark notifications as unread.
|
||||||
|
var CmdNotificationsMarkUnread = cli.Command{
|
||||||
|
Name: "unread",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Usage: "Mark all filtered or a specific notification as unread",
|
||||||
|
Description: "Mark all filtered or a specific notification as unread",
|
||||||
|
ArgsUsage: "[all | <notification id>]",
|
||||||
|
Flags: flags.NotificationFlags,
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
cmd := context.InitCommand(ctx)
|
||||||
|
filter, err := flags.NotificationStateFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !cmd.IsSet(flags.NotificationStateFlag.Name) {
|
||||||
|
filter = []string{string(gitea.NotifyStatusRead)}
|
||||||
|
}
|
||||||
|
return markNotificationAs(cmd, filter, gitea.NotifyStatusUnread)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdNotificationsMarkPinned will mark notifications as unread.
|
||||||
|
var CmdNotificationsMarkPinned = cli.Command{
|
||||||
|
Name: "pin",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "Mark all filtered or a specific notification as pinned",
|
||||||
|
Description: "Mark all filtered or a specific notification as pinned",
|
||||||
|
ArgsUsage: "[all | <notification id>]",
|
||||||
|
Flags: flags.NotificationFlags,
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
cmd := context.InitCommand(ctx)
|
||||||
|
filter, err := flags.NotificationStateFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !cmd.IsSet(flags.NotificationStateFlag.Name) {
|
||||||
|
filter = []string{string(gitea.NotifyStatusUnread)}
|
||||||
|
}
|
||||||
|
return markNotificationAs(cmd, filter, gitea.NotifyStatusPinned)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdNotificationsUnpin will mark pinned notifications as unread.
|
||||||
|
var CmdNotificationsUnpin = cli.Command{
|
||||||
|
Name: "unpin",
|
||||||
|
Usage: "Unpin all pinned or a specific notification",
|
||||||
|
Description: "Marks all pinned or a specific notification as read",
|
||||||
|
ArgsUsage: "[all | <notification id>]",
|
||||||
|
Flags: flags.NotificationFlags,
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
cmd := context.InitCommand(ctx)
|
||||||
|
filter := []string{string(gitea.NotifyStatusPinned)}
|
||||||
|
// NOTE: we implicitly mark it as read, to match web UI semantics. marking as unread might be more useful?
|
||||||
|
return markNotificationAs(cmd, filter, gitea.NotifyStatusRead)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func markNotificationAs(cmd *context.TeaContext, filterStates []string, targetState gitea.NotifyStatus) (err error) {
|
||||||
|
client := cmd.Login.Client()
|
||||||
|
subject := cmd.Args().First()
|
||||||
|
allRepos := cmd.Bool("mine")
|
||||||
|
|
||||||
|
states := []gitea.NotifyStatus{}
|
||||||
|
for _, s := range filterStates {
|
||||||
|
states = append(states, gitea.NotifyStatus(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch subject {
|
||||||
|
case "", "all":
|
||||||
|
opts := gitea.MarkNotificationOptions{Status: states, ToStatus: targetState}
|
||||||
|
|
||||||
|
if allRepos {
|
||||||
|
_, _, err = client.ReadNotifications(opts)
|
||||||
|
} else {
|
||||||
|
cmd.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
_, _, err = client.ReadRepoNotifications(cmd.Owner, cmd.Repo, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: print all affected notification subject URLs
|
||||||
|
// (not supported by API currently, https://github.com/go-gitea/gitea/issues/16797)
|
||||||
|
|
||||||
|
default:
|
||||||
|
id, err := utils.ArgToIndex(subject)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _, err = client.ReadNotification(id, targetState)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, _, err := client.GetNotification(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// FIXME: this is an API URL, we want to display a web ui link..
|
||||||
|
fmt.Println(n.Subject.URL)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
@ -5,9 +5,9 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"code.gitea.io/tea/cmd/organizations"
|
"code.gitea.io/tea/cmd/organizations"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -23,17 +23,26 @@ var CmdOrgs = cli.Command{
|
|||||||
Action: runOrganizations,
|
Action: runOrganizations,
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
&organizations.CmdOrganizationList,
|
&organizations.CmdOrganizationList,
|
||||||
|
&organizations.CmdOrganizationCreate,
|
||||||
&organizations.CmdOrganizationDelete,
|
&organizations.CmdOrganizationDelete,
|
||||||
},
|
},
|
||||||
|
Flags: organizations.CmdOrganizationList.Flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runOrganizations(ctx *cli.Context) error {
|
func runOrganizations(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
if ctx.Args().Len() == 1 {
|
if ctx.Args().Len() == 1 {
|
||||||
return runOrganizationDetail(ctx.Args().First())
|
return runOrganizationDetail(ctx)
|
||||||
}
|
}
|
||||||
return organizations.RunOrganizationList(ctx)
|
return organizations.RunOrganizationList(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runOrganizationDetail(path string) error {
|
func runOrganizationDetail(ctx *context.TeaContext) error {
|
||||||
return fmt.Errorf("Not yet implemented")
|
org, _, err := ctx.Login.Client().GetOrg(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.OrganizationDetails(org)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
90
cmd/organizations/create.go
Normal file
90
cmd/organizations/create.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package organizations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdOrganizationCreate represents a sub command of organizations to delete a given user organization
|
||||||
|
var CmdOrganizationCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create an organization",
|
||||||
|
Description: "Create an organization",
|
||||||
|
Action: RunOrganizationCreate,
|
||||||
|
ArgsUsage: "<organization name>",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "website",
|
||||||
|
Aliases: []string{"w"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "location",
|
||||||
|
Aliases: []string{"L"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "visibility",
|
||||||
|
Aliases: []string{"v"},
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "repo-admins-can-change-team-access",
|
||||||
|
},
|
||||||
|
&flags.LoginFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunOrganizationCreate sets up a new organization
|
||||||
|
func RunOrganizationCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
if ctx.Args().Len() < 1 {
|
||||||
|
return fmt.Errorf("You have to specify the organization name you want to create")
|
||||||
|
}
|
||||||
|
|
||||||
|
var visibility gitea.VisibleType
|
||||||
|
switch ctx.String("visibility") {
|
||||||
|
case "", "public":
|
||||||
|
visibility = gitea.VisibleTypePublic
|
||||||
|
case "private":
|
||||||
|
visibility = gitea.VisibleTypePrivate
|
||||||
|
case "limited":
|
||||||
|
visibility = gitea.VisibleTypeLimited
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown visibility '%s'", ctx.String("visibility"))
|
||||||
|
}
|
||||||
|
|
||||||
|
org, _, err := ctx.Login.Client().CreateOrg(gitea.CreateOrgOption{
|
||||||
|
Name: ctx.Args().First(),
|
||||||
|
// FullName: , // not really meaningful for orgs (not displayed in webui, use description instead?)
|
||||||
|
Description: ctx.String("description"),
|
||||||
|
Website: ctx.String("website"),
|
||||||
|
Location: ctx.String("location"),
|
||||||
|
RepoAdminChangeTeamAccess: ctx.Bool("repo-admins-can-change-team-access"),
|
||||||
|
Visibility: visibility,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.OrganizationDetails(org)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
@ -7,6 +7,7 @@ package organizations
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
@ -19,6 +20,10 @@ var CmdOrganizationDelete = cli.Command{
|
|||||||
Description: "Delete users organizations",
|
Description: "Delete users organizations",
|
||||||
ArgsUsage: "<organization name>",
|
ArgsUsage: "<organization name>",
|
||||||
Action: RunOrganizationDelete,
|
Action: RunOrganizationDelete,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&flags.LoginFlag,
|
||||||
|
&flags.RemoteFlag,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunOrganizationDelete delete user organization
|
// RunOrganizationDelete delete user organization
|
||||||
|
@ -19,6 +19,7 @@ var CmdOrganizationList = cli.Command{
|
|||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
Usage: "List Organizations",
|
Usage: "List Organizations",
|
||||||
Description: "List users organizations",
|
Description: "List users organizations",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: RunOrganizationList,
|
Action: RunOrganizationList,
|
||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&flags.PaginationPageFlag,
|
&flags.PaginationPageFlag,
|
||||||
|
@ -30,7 +30,7 @@ var CmdPulls = cli.Command{
|
|||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "comments",
|
Name: "comments",
|
||||||
Usage: "Wether to display comments (will prompt if not provided & run interactively)",
|
Usage: "Whether to display comments (will prompt if not provided & run interactively)",
|
||||||
},
|
},
|
||||||
}, pulls.CmdPullsList.Flags...),
|
}, pulls.CmdPullsList.Flags...),
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
@ -43,6 +43,7 @@ var CmdPulls = cli.Command{
|
|||||||
&pulls.CmdPullsReview,
|
&pulls.CmdPullsReview,
|
||||||
&pulls.CmdPullsApprove,
|
&pulls.CmdPullsApprove,
|
||||||
&pulls.CmdPullsReject,
|
&pulls.CmdPullsReject,
|
||||||
|
&pulls.CmdPullsMerge,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +71,9 @@ func runPullDetail(cmd *cli.Context, index string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
reviews, _, err := client.ListPullReviews(ctx.Owner, ctx.Repo, idx, gitea.ListPullReviewsOptions{})
|
reviews, _, err := client.ListPullReviews(ctx.Owner, ctx.Repo, idx, gitea.ListPullReviewsOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("error while loading reviews: %v\n", err)
|
fmt.Printf("error while loading reviews: %v\n", err)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,10 @@ var CmdPullsCheckout = cli.Command{
|
|||||||
|
|
||||||
func runPullsCheckout(cmd *cli.Context) error {
|
func runPullsCheckout(cmd *cli.Context) error {
|
||||||
ctx := context.InitCommand(cmd)
|
ctx := context.InitCommand(cmd)
|
||||||
ctx.Ensure(context.CtxRequirement{LocalRepo: true})
|
ctx.Ensure(context.CtxRequirement{
|
||||||
|
LocalRepo: true,
|
||||||
|
RemoteRepo: true,
|
||||||
|
})
|
||||||
if ctx.Args().Len() != 1 {
|
if ctx.Args().Len() != 1 {
|
||||||
return fmt.Errorf("Must specify a PR index")
|
return fmt.Errorf("Must specify a PR index")
|
||||||
}
|
}
|
||||||
|
@ -18,28 +18,27 @@ var CmdPullsCreate = cli.Command{
|
|||||||
Name: "create",
|
Name: "create",
|
||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
Usage: "Create a pull-request",
|
Usage: "Create a pull-request",
|
||||||
Description: "Create a pull-request",
|
Description: "Create a pull-request in the current repo",
|
||||||
Action: runPullsCreate,
|
Action: runPullsCreate,
|
||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "head",
|
Name: "head",
|
||||||
Usage: "Set head branch (default is current one)",
|
Usage: "Branch name of the PR source (default is current one). To specify a different head repo, use <user>:<branch>",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "base",
|
Name: "base",
|
||||||
Aliases: []string{"b"},
|
Aliases: []string{"b"},
|
||||||
Usage: "Set base branch (default is default branch)",
|
Usage: "Branch name of the PR target (default is repos default branch)",
|
||||||
},
|
},
|
||||||
}, flags.IssuePREditFlags...),
|
}, flags.IssuePREditFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPullsCreate(cmd *cli.Context) error {
|
func runPullsCreate(cmd *cli.Context) error {
|
||||||
ctx := context.InitCommand(cmd)
|
ctx := context.InitCommand(cmd)
|
||||||
ctx.Ensure(context.CtxRequirement{LocalRepo: true})
|
|
||||||
|
|
||||||
// no args -> interactive mode
|
// no args -> interactive mode
|
||||||
if ctx.NumFlags() == 0 {
|
if ctx.NumFlags() == 0 {
|
||||||
return interact.CreatePull(ctx.Login, ctx.Owner, ctx.Repo)
|
return interact.CreatePull(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// else use args to create PR
|
// else use args to create PR
|
||||||
@ -49,9 +48,7 @@ func runPullsCreate(cmd *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return task.CreatePull(
|
return task.CreatePull(
|
||||||
ctx.Login,
|
ctx,
|
||||||
ctx.Owner,
|
|
||||||
ctx.Repo,
|
|
||||||
ctx.String("base"),
|
ctx.String("base"),
|
||||||
ctx.String("head"),
|
ctx.String("head"),
|
||||||
opts,
|
opts,
|
||||||
|
@ -13,14 +13,19 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var pullFieldsFlag = flags.FieldsFlag(print.PullFields, []string{
|
||||||
|
"index", "title", "state", "author", "milestone", "updated", "labels",
|
||||||
|
})
|
||||||
|
|
||||||
// CmdPullsList represents a sub command of issues to list pulls
|
// CmdPullsList represents a sub command of issues to list pulls
|
||||||
var CmdPullsList = cli.Command{
|
var CmdPullsList = cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
Usage: "List pull requests of the repository",
|
Usage: "List pull requests of the repository",
|
||||||
Description: `List pull requests of the repository`,
|
Description: `List pull requests of the repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: RunPullsList,
|
Action: RunPullsList,
|
||||||
Flags: flags.IssuePRFlags,
|
Flags: append([]cli.Flag{pullFieldsFlag}, flags.PRListingFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunPullsList return list of pulls
|
// RunPullsList return list of pulls
|
||||||
@ -46,6 +51,11 @@ func RunPullsList(cmd *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
print.PullsList(prs, ctx.Output)
|
fields, err := pullFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.PullsList(prs, ctx.Output, fields)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
70
cmd/pulls/merge.go
Normal file
70
cmd/pulls/merge.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsMerge merges a PR
|
||||||
|
var CmdPullsMerge = cli.Command{
|
||||||
|
Name: "merge",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Merge a pull request",
|
||||||
|
Description: "Merge a pull request",
|
||||||
|
ArgsUsage: "<pull index>",
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "style",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Usage: "Kind of merge to perform: merge, rebase, squash, rebase-merge",
|
||||||
|
Value: "merge",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "title",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Usage: "Merge commit title",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "message",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Merge commit message",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
if ctx.Args().Len() != 1 {
|
||||||
|
return fmt.Errorf("Must specify a PR index")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
success, _, err := ctx.Login.Client().MergePullRequest(ctx.Owner, ctx.Repo, idx, gitea.MergePullRequestOption{
|
||||||
|
Style: gitea.MergeStyle(ctx.String("style")),
|
||||||
|
Title: ctx.String("title"),
|
||||||
|
Message: ctx.String("message"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !success {
|
||||||
|
return fmt.Errorf("Failed to merge PR. Is it still open?")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
@ -19,6 +19,7 @@ var CmdReleases = cli.Command{
|
|||||||
Category: catEntities,
|
Category: catEntities,
|
||||||
Usage: "Manage releases",
|
Usage: "Manage releases",
|
||||||
Description: "Manage releases",
|
Description: "Manage releases",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: releases.RunReleasesList,
|
Action: releases.RunReleasesList,
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
&releases.CmdReleaseList,
|
&releases.CmdReleaseList,
|
||||||
|
@ -22,16 +22,17 @@ var CmdReleaseCreate = cli.Command{
|
|||||||
Name: "create",
|
Name: "create",
|
||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
Usage: "Create a release",
|
Usage: "Create a release",
|
||||||
Description: `Create a release`,
|
Description: `Create a release for a new or existing git tag`,
|
||||||
|
ArgsUsage: "[<tag>]",
|
||||||
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. If the tag does not exist yet, it will be created by Gitea",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "target",
|
Name: "target",
|
||||||
Usage: "Target refs, branch name or commit id",
|
Usage: "Target branch name or commit hash. Defaults to the default branch of the repo",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "title",
|
Name: "title",
|
||||||
@ -56,7 +57,7 @@ var CmdReleaseCreate = cli.Command{
|
|||||||
&cli.StringSliceFlag{
|
&cli.StringSliceFlag{
|
||||||
Name: "asset",
|
Name: "asset",
|
||||||
Aliases: []string{"a"},
|
Aliases: []string{"a"},
|
||||||
Usage: "List of files to attach",
|
Usage: "Path to file attachment. Can be specified multiple times",
|
||||||
},
|
},
|
||||||
}, flags.AllDefaultFlags...),
|
}, flags.AllDefaultFlags...),
|
||||||
}
|
}
|
||||||
@ -65,8 +66,16 @@ func runReleaseCreate(cmd *cli.Context) error {
|
|||||||
ctx := context.InitCommand(cmd)
|
ctx := context.InitCommand(cmd)
|
||||||
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
tag := ctx.String("tag")
|
||||||
|
if cmd.Args().Present() {
|
||||||
|
if len(tag) != 0 {
|
||||||
|
return fmt.Errorf("ambiguous arguments: provide tagname via --tag or argument, but not both")
|
||||||
|
}
|
||||||
|
tag = cmd.Args().First()
|
||||||
|
}
|
||||||
|
|
||||||
release, resp, err := ctx.Login.Client().CreateRelease(ctx.Owner, ctx.Repo, gitea.CreateReleaseOption{
|
release, resp, err := ctx.Login.Client().CreateRelease(ctx.Owner, ctx.Repo, gitea.CreateReleaseOption{
|
||||||
TagName: ctx.String("tag"),
|
TagName: tag,
|
||||||
Target: ctx.String("target"),
|
Target: ctx.String("target"),
|
||||||
Title: ctx.String("title"),
|
Title: ctx.String("title"),
|
||||||
Note: ctx.String("note"),
|
Note: ctx.String("note"),
|
||||||
|
@ -54,10 +54,6 @@ func runReleaseDelete(cmd *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if release == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = client.DeleteRelease(ctx.Owner, ctx.Repo, release.ID)
|
_, err = client.DeleteRelease(ctx.Owner, ctx.Repo, release.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -72,10 +72,6 @@ func runReleaseEdit(cmd *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if release == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var isDraft, isPre *bool
|
var isDraft, isPre *bool
|
||||||
if ctx.IsSet("draft") {
|
if ctx.IsSet("draft") {
|
||||||
isDraft = gitea.OptionalBool(strings.ToLower(ctx.String("draft"))[:1] == "t")
|
isDraft = gitea.OptionalBool(strings.ToLower(ctx.String("draft"))[:1] == "t")
|
||||||
|
@ -21,6 +21,7 @@ var CmdReleaseList = cli.Command{
|
|||||||
Aliases: []string{"ls"},
|
Aliases: []string{"ls"},
|
||||||
Usage: "List Releases",
|
Usage: "List Releases",
|
||||||
Description: "List Releases",
|
Description: "List Releases",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: RunReleasesList,
|
Action: RunReleasesList,
|
||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&flags.PaginationPageFlag,
|
&flags.PaginationPageFlag,
|
||||||
@ -45,19 +46,19 @@ func RunReleasesList(cmd *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) {
|
func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) {
|
||||||
rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{})
|
rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(rl) == 0 {
|
if len(rl) == 0 {
|
||||||
fmt.Println("Repo does not have any release")
|
return nil, fmt.Errorf("Repo does not have any release")
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
for _, r := range rl {
|
for _, r := range rl {
|
||||||
if r.TagName == tag {
|
if r.TagName == tag {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("Release tag does not exist")
|
return nil, fmt.Errorf("Release tag does not exist")
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,8 @@ var CmdRepos = cli.Command{
|
|||||||
&repos.CmdReposList,
|
&repos.CmdReposList,
|
||||||
&repos.CmdReposSearch,
|
&repos.CmdReposSearch,
|
||||||
&repos.CmdRepoCreate,
|
&repos.CmdRepoCreate,
|
||||||
|
&repos.CmdRepoCreateFromTemplate,
|
||||||
|
&repos.CmdRepoFork,
|
||||||
},
|
},
|
||||||
Flags: repos.CmdReposListFlags,
|
Flags: repos.CmdReposListFlags,
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ var CmdRepoCreate = cli.Command{
|
|||||||
Aliases: []string{"c"},
|
Aliases: []string{"c"},
|
||||||
Usage: "Create a repository",
|
Usage: "Create a repository",
|
||||||
Description: "Create a repository",
|
Description: "Create a repository",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runRepoCreate,
|
Action: runRepoCreate,
|
||||||
Flags: append([]cli.Flag{
|
Flags: append([]cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
@ -79,6 +80,14 @@ var CmdRepoCreate = cli.Command{
|
|||||||
Required: false,
|
Required: false,
|
||||||
Usage: "use custom default branch (need --init)",
|
Usage: "use custom default branch (need --init)",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "template",
|
||||||
|
Usage: "make repo a template repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "trustmodel",
|
||||||
|
Usage: "select trust model (committer,collaborator,collaborator+committer)",
|
||||||
|
},
|
||||||
}, flags.LoginOutputFlags...),
|
}, flags.LoginOutputFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +97,22 @@ func runRepoCreate(cmd *cli.Context) error {
|
|||||||
var (
|
var (
|
||||||
repo *gitea.Repository
|
repo *gitea.Repository
|
||||||
err error
|
err error
|
||||||
|
trustmodel gitea.TrustModel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ctx.IsSet("trustmodel") {
|
||||||
|
switch ctx.String("trustmodel") {
|
||||||
|
case "committer":
|
||||||
|
trustmodel = gitea.TrustModelCommitter
|
||||||
|
case "collaborator":
|
||||||
|
trustmodel = gitea.TrustModelCollaborator
|
||||||
|
case "collaborator+committer":
|
||||||
|
trustmodel = gitea.TrustModelCollaboratorCommitter
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown trustmodel type '%s'", ctx.String("trustmodel"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opts := gitea.CreateRepoOption{
|
opts := gitea.CreateRepoOption{
|
||||||
Name: ctx.String("name"),
|
Name: ctx.String("name"),
|
||||||
Description: ctx.String("description"),
|
Description: ctx.String("description"),
|
||||||
@ -99,6 +123,8 @@ func runRepoCreate(cmd *cli.Context) error {
|
|||||||
License: ctx.String("license"),
|
License: ctx.String("license"),
|
||||||
Readme: ctx.String("readme"),
|
Readme: ctx.String("readme"),
|
||||||
DefaultBranch: ctx.String("branch"),
|
DefaultBranch: ctx.String("branch"),
|
||||||
|
Template: ctx.Bool("template"),
|
||||||
|
TrustModel: trustmodel,
|
||||||
}
|
}
|
||||||
if len(ctx.String("owner")) != 0 {
|
if len(ctx.String("owner")) != 0 {
|
||||||
repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts)
|
repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts)
|
||||||
|
121
cmd/repos/create_from_template.go
Normal file
121
cmd/repos/create_from_template.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoCreateFromTemplate represents a sub command of repos to generate one from a template repo
|
||||||
|
var CmdRepoCreateFromTemplate = cli.Command{
|
||||||
|
Name: "create-from-template",
|
||||||
|
Aliases: []string{"ct"},
|
||||||
|
Usage: "Create a repository based on an existing template",
|
||||||
|
Description: "Create a repository based on an existing template",
|
||||||
|
Action: runRepoCreateFromTemplate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "template",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "source template to copy from",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "name of new repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner",
|
||||||
|
Aliases: []string{"O"},
|
||||||
|
Usage: "name of repo owner",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "private",
|
||||||
|
Usage: "make new repo private",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Aliases: []string{"desc"},
|
||||||
|
Usage: "add custom description to repo",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "content",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy git content from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "githooks",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy git hooks from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "avatar",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy repo avatar from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "labels",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy repo labels from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "topics",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy topics from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "webhooks",
|
||||||
|
Usage: "copy webhooks from template",
|
||||||
|
},
|
||||||
|
}, flags.LoginOutputFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoCreateFromTemplate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
templateOwner, templateRepo := utils.GetOwnerAndRepo(ctx.String("template"), ctx.Login.User)
|
||||||
|
owner := ctx.Login.User
|
||||||
|
if ctx.IsSet("owner") {
|
||||||
|
owner = ctx.String("owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := gitea.CreateRepoFromTemplateOption{
|
||||||
|
Name: ctx.String("name"),
|
||||||
|
Owner: owner,
|
||||||
|
Description: ctx.String("description"),
|
||||||
|
Private: ctx.Bool("private"),
|
||||||
|
GitContent: ctx.Bool("content"),
|
||||||
|
GitHooks: ctx.Bool("githooks"),
|
||||||
|
Avatar: ctx.Bool("avatar"),
|
||||||
|
Labels: ctx.Bool("labels"),
|
||||||
|
Topics: ctx.Bool("topics"),
|
||||||
|
Webhooks: ctx.Bool("webhooks"),
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, _, err := client.CreateRepoFromTemplate(templateOwner, templateRepo, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.RepoDetails(repo, topics)
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", repo.HTMLURL)
|
||||||
|
return nil
|
||||||
|
}
|
59
cmd/repos/fork.go
Normal file
59
cmd/repos/fork.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoFork represents a sub command of repos to fork an existing repo
|
||||||
|
var CmdRepoFork = cli.Command{
|
||||||
|
Name: "fork",
|
||||||
|
Aliases: []string{"f"},
|
||||||
|
Usage: "Fork an existing repository",
|
||||||
|
Description: "Create a repository from an existing repo",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runRepoFork,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner",
|
||||||
|
Aliases: []string{"O"},
|
||||||
|
Usage: "name of fork's owner, defaults to current user",
|
||||||
|
},
|
||||||
|
}, flags.LoginRepoFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoFork(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
opts := gitea.CreateForkOption{}
|
||||||
|
if ctx.IsSet("owner") {
|
||||||
|
owner := ctx.String("owner")
|
||||||
|
opts.Organization = &owner
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, _, err := client.CreateFork(ctx.Owner, ctx.Repo, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.RepoDetails(repo, topics)
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", repo.HTMLURL)
|
||||||
|
return nil
|
||||||
|
}
|
@ -13,6 +13,10 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var repoFieldsFlag = flags.FieldsFlag(print.RepoFields, []string{
|
||||||
|
"owner", "name", "type", "ssh",
|
||||||
|
})
|
||||||
|
|
||||||
// CmdReposListFlags contains all flags needed for repo listing
|
// CmdReposListFlags contains all flags needed for repo listing
|
||||||
var CmdReposListFlags = append([]cli.Flag{
|
var CmdReposListFlags = append([]cli.Flag{
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
@ -27,9 +31,7 @@ var CmdReposListFlags = append([]cli.Flag{
|
|||||||
Required: false,
|
Required: false,
|
||||||
Usage: "List your starred repos instead",
|
Usage: "List your starred repos instead",
|
||||||
},
|
},
|
||||||
flags.FieldsFlag(print.RepoFields, []string{
|
repoFieldsFlag,
|
||||||
"owner", "name", "type", "ssh",
|
|
||||||
}),
|
|
||||||
&typeFilterFlag,
|
&typeFilterFlag,
|
||||||
&flags.PaginationPageFlag,
|
&flags.PaginationPageFlag,
|
||||||
&flags.PaginationLimitFlag,
|
&flags.PaginationLimitFlag,
|
||||||
@ -82,7 +84,7 @@ func RunReposList(cmd *cli.Context) error {
|
|||||||
reposFiltered = filterReposByType(rps, typeFilter)
|
reposFiltered = filterReposByType(rps, typeFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fields, err := flags.GetFields(cmd, print.RepoFields)
|
fields, err := repoFieldsFlag.GetValues(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,7 @@ var CmdReposSearch = cli.Command{
|
|||||||
Required: false,
|
Required: false,
|
||||||
Usage: "Filter archived repos (true|false)",
|
Usage: "Filter archived repos (true|false)",
|
||||||
},
|
},
|
||||||
flags.FieldsFlag(print.RepoFields, []string{
|
repoFieldsFlag,
|
||||||
"owner", "name", "type", "ssh",
|
|
||||||
}),
|
|
||||||
&flags.PaginationPageFlag,
|
&flags.PaginationPageFlag,
|
||||||
&flags.PaginationLimitFlag,
|
&flags.PaginationLimitFlag,
|
||||||
}, flags.LoginOutputFlags...),
|
}, flags.LoginOutputFlags...),
|
||||||
@ -125,7 +123,7 @@ func runReposSearch(cmd *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fields, err := flags.GetFields(cmd, nil)
|
fields, err := repoFieldsFlag.GetValues(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,17 @@ import (
|
|||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NOTE: not using NewCsvFlag, as we don't want an alias & default value.
|
||||||
|
var timeFieldsFlag = &flags.CsvFlag{
|
||||||
|
AvailableFields: print.TrackedTimeFields,
|
||||||
|
StringFlag: cli.StringFlag{
|
||||||
|
Name: "fields",
|
||||||
|
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
|
||||||
|
%s
|
||||||
|
`, strings.Join(print.TrackedTimeFields, ",")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// CmdTrackedTimesList represents a sub command of times to list them
|
// CmdTrackedTimesList represents a sub command of times to list them
|
||||||
var CmdTrackedTimesList = cli.Command{
|
var CmdTrackedTimesList = cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
@ -53,12 +64,7 @@ Depending on your permissions on the repository, only your own tracked times mig
|
|||||||
Aliases: []string{"m"},
|
Aliases: []string{"m"},
|
||||||
Usage: "Show all times tracked by you across all repositories (overrides command arguments)",
|
Usage: "Show all times tracked by you across all repositories (overrides command arguments)",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
timeFieldsFlag,
|
||||||
Name: "fields",
|
|
||||||
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
|
|
||||||
%s
|
|
||||||
`, strings.Join(print.TrackedTimeFields, ",")),
|
|
||||||
},
|
|
||||||
}, flags.AllDefaultFlags...),
|
}, flags.AllDefaultFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +122,7 @@ func RunTimesList(cmd *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ctx.IsSet("fields") {
|
if ctx.IsSet("fields") {
|
||||||
if fields, err = flags.GetFields(cmd, print.TrackedTimeFields); err != nil {
|
if fields, err = timeFieldsFlag.GetValues(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
cmd/whoami.go
Normal file
28
cmd/whoami.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdWhoami represents the command to show current logged in user
|
||||||
|
var CmdWhoami = cli.Command{
|
||||||
|
Name: "whoami",
|
||||||
|
Category: catMisc,
|
||||||
|
Description: `For debugging purposes, show the user that is currently logged in.`,
|
||||||
|
Usage: "Show current logged in user",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
user, _, _ := client.GetMyUserInfo()
|
||||||
|
print.UserDetails(user)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
84
go.mod
84
go.mod
@ -1,38 +1,68 @@
|
|||||||
module code.gitea.io/tea
|
module code.gitea.io/tea
|
||||||
|
|
||||||
go 1.13
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.gitea.io/gitea-vet v0.2.1
|
code.gitea.io/gitea-vet v0.2.1
|
||||||
code.gitea.io/sdk/gitea v0.13.1-0.20210304201955-ff82113459b5
|
code.gitea.io/sdk/gitea v0.15.1-0.20220831004139-a0127ed0e7fe
|
||||||
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b
|
gitea.com/noerw/unidiff-comments v0.0.0-20220822113322-50f4daa0e35c
|
||||||
github.com/AlecAivazis/survey/v2 v2.2.8
|
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
github.com/adrg/xdg v0.4.0
|
||||||
github.com/adrg/xdg v0.3.1
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
||||||
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e
|
github.com/charmbracelet/glamour v0.5.0
|
||||||
github.com/charmbracelet/glamour v0.2.0
|
github.com/enescakir/emoji v1.0.0
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
github.com/go-git/go-git/v5 v5.4.2
|
||||||
github.com/go-git/go-git/v5 v5.2.0
|
github.com/muesli/termenv v0.12.0
|
||||||
github.com/imdario/mergo v0.3.11 // indirect
|
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
|
||||||
github.com/muesli/termenv v0.7.4
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/urfave/cli/v2 v2.16.3
|
||||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3 // indirect
|
|
||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
|
|
||||||
golang.org/x/text v0.3.5 // indirect
|
|
||||||
golang.org/x/tools v0.1.0 // indirect
|
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/charmbracelet/glamour => github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2
|
require (
|
||||||
|
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||||
|
github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 // indirect
|
||||||
|
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||||
|
github.com/alecthomas/chroma v0.10.0 // indirect
|
||||||
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
|
github.com/cloudflare/circl v1.2.0 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||||
|
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||||
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
|
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||||
|
github.com/go-git/gcfg v1.5.0 // indirect
|
||||||
|
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||||
|
github.com/gorilla/css v1.0.0 // indirect
|
||||||
|
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.13 // indirect
|
||||||
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.20 // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.2 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/sergi/go-diff v1.2.0 // indirect
|
||||||
|
github.com/xanzy/ssh-agent v0.3.2 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
|
github.com/yuin/goldmark v1.4.14 // indirect
|
||||||
|
github.com/yuin/goldmark-emoji v1.0.1 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220913153101-76c7481b5158 // indirect
|
||||||
|
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
257
go.sum
257
go.sum
@ -1,122 +1,132 @@
|
|||||||
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
|
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
|
||||||
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
||||||
code.gitea.io/sdk/gitea v0.13.1-0.20210304201955-ff82113459b5 h1:va0KddYHN8bH6MCUaWf5e4p+il55blUw5J0ha5vTMaQ=
|
code.gitea.io/sdk/gitea v0.15.1-0.20220831004139-a0127ed0e7fe h1:PeLyxnUZE85QuJtBZ4P8qCQcgWG5Ked67mlNgr0WkCQ=
|
||||||
code.gitea.io/sdk/gitea v0.13.1-0.20210304201955-ff82113459b5/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs=
|
code.gitea.io/sdk/gitea v0.15.1-0.20220831004139-a0127ed0e7fe/go.mod h1:aRmrQC3CAHdJAU1LQt0C9zqzqI8tUB/5oQtNE746aYE=
|
||||||
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b h1:CLYsMGcGLohESQDMth+RgJ4cB3CCHToxnj0zBbvB3sE=
|
gitea.com/noerw/unidiff-comments v0.0.0-20220822113322-50f4daa0e35c h1:8fTkq2UaVkLHZCF+iB4wTxINmVAToe2geZGayk9LMbA=
|
||||||
gitea.com/noerw/unidiff-comments v0.0.0-20201219085024-64aec5658f2b/go.mod h1:Fc8iyPm4NINRWujeIk2bTfcbGc4ZYY29/oMAAGcr4qI=
|
gitea.com/noerw/unidiff-comments v0.0.0-20220822113322-50f4daa0e35c/go.mod h1:Fc8iyPm4NINRWujeIk2bTfcbGc4ZYY29/oMAAGcr4qI=
|
||||||
github.com/AlecAivazis/survey/v2 v2.2.8 h1:TgxCwybKdBckmC+/P9/5h49rw/nAHe/itZL0dgHs+Q0=
|
github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
|
||||||
github.com/AlecAivazis/survey/v2 v2.2.8/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk=
|
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
|
|
||||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
|
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||||
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/adrg/xdg v0.3.1 h1:uIyL9BYfXaFgDyVRKE8wjtm6ETQULweQqTofphEFJYY=
|
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||||
github.com/adrg/xdg v0.3.1/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ=
|
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErIPzRrnogAXYwSoU7txA977LjDGrbkewJbg=
|
||||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
|
||||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||||
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE=
|
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||||
github.com/alecthomas/chroma v0.8.1/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
|
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
|
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||||
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
|
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||||
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
|
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||||
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
|
|
||||||
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
|
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e h1:OjdSMCht0ZVX7IH0nTdf00xEustvbtUGRgMh3gbdmOg=
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
||||||
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
|
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
|
github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/charmbracelet/glamour v0.5.0 h1:wu15ykPdB7X6chxugG/NNfDUbyyrCLV9XBalj5wdu3g=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
github.com/charmbracelet/glamour v0.5.0/go.mod h1:9ZRtG19AUIzcTm7FGLGbq3D5WKQ5UyZBbQsMQN0XIqc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||||
|
github.com/cloudflare/circl v1.2.0 h1:NheeISPSUcYftKlfrLuOo4T62FkmD4t4jviLfFFYaec=
|
||||||
|
github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8M+INXlMk=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
|
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
|
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
|
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
||||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
|
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
|
||||||
|
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
|
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||||
|
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
|
||||||
|
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
|
||||||
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-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||||
|
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||||
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
|
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
|
||||||
github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2JgI=
|
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
||||||
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
|
github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
|
||||||
|
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
|
||||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||||
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
|
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||||
|
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
|
|
||||||
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||||
|
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
|
github.com/microcosm-cc/bluemonday v1.0.17/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
|
github.com/microcosm-cc/bluemonday v1.0.20 h1:flpzsq4KU3QIYAYGV/szUat7H+GPOXR0B2JU5A1Wp8Y=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/muesli/reflow v0.2.0 h1:2o0UBJPHHH4fa2GCXU4Rg4DwOtWPMekCeyc5EWbAQp0=
|
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||||
github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8=
|
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||||
github.com/muesli/termenv v0.7.4 h1:/pBqvU5CpkY53tU0vVn+xgs2ZTX63aH5nY+SSps5Xa8=
|
github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw=
|
||||||
github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc=
|
github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2 h1:ACjOTGUGi7rt3JQU9GIFFs8sueFGShy6GcGjQhMmKjs=
|
|
||||||
github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2/go.mod h1:WIVFX8Y2VIK1Y/1qtXYL/Vvzqlcbo3VgVop9i2piPkE=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@ -125,111 +135,124 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
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/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
|
||||||
|
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
|
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
|
||||||
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA=
|
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597 h1:nZY1S2jo+VtDrUfjO9XYI137O41hhRkxZNV5Fb5ixCA=
|
||||||
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw=
|
github.com/seletskiy/tplutil v0.0.0-20200921103632-f880f6245597/go.mod h1:F8CBHSOjnzjx9EeXyWJTAzJyVxN+Y8JH2WjLMn4utiw=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||||
|
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
|
||||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
|
||||||
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
|
|
||||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||||
|
github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM=
|
||||||
|
github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.1 h1:eVwehsLsZlCJCwXyGLgg+Q4iFWE/eTIMG0e8waCmm/I=
|
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||||
github.com/yuin/goldmark v1.3.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.14 h1:jwww1XQfhJN7Zm+/a1ZA/3WUiEBEroYFNTiV3dKwM8U=
|
||||||
|
github.com/yuin/goldmark v1.4.14/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
|
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
|
||||||
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
|
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
|
||||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||||
|
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||||
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
|
||||||
|
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3 h1:RdE7htvBru4I4VZQofQjCZk5W9+aLNlSF5n0zgVwm8s=
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210305034016-7844c3c200c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220913153101-76c7481b5158 h1:XQphkCZeKYaMRSo28HqvvNYuLOoM5CIOOvTZfthvTgI=
|
||||||
|
golang.org/x/sys v0.0.0-20220913153101-76c7481b5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
|
||||||
|
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
39
main.go
39
main.go
@ -8,6 +8,7 @@ package main // import "code.gitea.io/tea"
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/tea/cmd"
|
"code.gitea.io/tea/cmd"
|
||||||
@ -21,6 +22,9 @@ var Version = "development"
|
|||||||
// Tags holds the build tags used
|
// Tags holds the build tags used
|
||||||
var Tags = ""
|
var Tags = ""
|
||||||
|
|
||||||
|
// SDK holds the sdk version from go.mod
|
||||||
|
var SDK = ""
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// make parsing tea --version easier, by printing /just/ the version string
|
// make parsing tea --version easier, by printing /just/ the version string
|
||||||
cli.VersionPrinter = func(c *cli.Context) { fmt.Fprintln(c.App.Writer, c.App.Version) }
|
cli.VersionPrinter = func(c *cli.Context) { fmt.Fprintln(c.App.Writer, c.App.Version) }
|
||||||
@ -30,11 +34,12 @@ func main() {
|
|||||||
app.Usage = "command line tool to interact with Gitea"
|
app.Usage = "command line tool to interact with Gitea"
|
||||||
app.Description = appDescription
|
app.Description = appDescription
|
||||||
app.CustomAppHelpTemplate = helpTemplate
|
app.CustomAppHelpTemplate = helpTemplate
|
||||||
app.Version = Version + formatBuiltWith(Tags)
|
app.Version = formatVersion()
|
||||||
app.Commands = []*cli.Command{
|
app.Commands = []*cli.Command{
|
||||||
&cmd.CmdLogin,
|
&cmd.CmdLogin,
|
||||||
&cmd.CmdLogout,
|
&cmd.CmdLogout,
|
||||||
&cmd.CmdAutocomplete,
|
&cmd.CmdAutocomplete,
|
||||||
|
&cmd.CmdWhoami,
|
||||||
|
|
||||||
&cmd.CmdIssues,
|
&cmd.CmdIssues,
|
||||||
&cmd.CmdPulls,
|
&cmd.CmdPulls,
|
||||||
@ -48,6 +53,9 @@ func main() {
|
|||||||
|
|
||||||
&cmd.CmdOpen,
|
&cmd.CmdOpen,
|
||||||
&cmd.CmdNotifications,
|
&cmd.CmdNotifications,
|
||||||
|
&cmd.CmdRepoClone,
|
||||||
|
|
||||||
|
&cmd.CmdAdmin,
|
||||||
}
|
}
|
||||||
app.EnableBashCompletion = true
|
app.EnableBashCompletion = true
|
||||||
err := app.Run(os.Args)
|
err := app.Run(os.Args)
|
||||||
@ -59,18 +67,29 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatBuiltWith(Tags string) string {
|
func formatVersion() string {
|
||||||
if len(Tags) == 0 {
|
version := fmt.Sprintf("Version: %s\tgolang: %s",
|
||||||
return ""
|
bold(Version),
|
||||||
|
strings.ReplaceAll(runtime.Version(), "go", ""))
|
||||||
|
|
||||||
|
if len(Tags) != 0 {
|
||||||
|
version += fmt.Sprintf("\tbuilt with: %s", strings.Replace(Tags, " ", ", ", -1))
|
||||||
}
|
}
|
||||||
|
|
||||||
return " built with: " + strings.Replace(Tags, " ", ", ", -1)
|
if len(SDK) != 0 {
|
||||||
|
version += fmt.Sprintf("\tgo-sdk: %s", SDK)
|
||||||
}
|
}
|
||||||
|
|
||||||
var appDescription = `tea is a productivity helper for Gitea. It can be used to manage most entities on one
|
return version
|
||||||
or multiple Gitea instances and provides local helpers like 'tea pull checkout'.
|
}
|
||||||
tea makes use of context provided by the repository in $PWD if available, but is still
|
|
||||||
usable independently of $PWD. Configuration is persisted in $XDG_CONFIG_HOME/tea.
|
var appDescription = `tea is a productivity helper for Gitea. It can be used to manage most entities on
|
||||||
|
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.
|
||||||
|
|
||||||
|
tea tries to make use of context provided by the repository in $PWD if available.
|
||||||
|
tea works best in a upstream/fork workflow, when the local main branch tracks the
|
||||||
|
upstream repo. tea assumes that local git state is published on the remote before
|
||||||
|
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.
|
||||||
`
|
`
|
||||||
|
|
||||||
var helpTemplate = bold(`
|
var helpTemplate = bold(`
|
||||||
@ -108,7 +127,7 @@ var helpTemplate = bold(`
|
|||||||
tea open milestones # open web ui for milestones
|
tea open milestones # open web ui for milestones
|
||||||
|
|
||||||
# send gitea desktop notifications every 5 minutes (bash + libnotify)
|
# send gitea desktop notifications every 5 minutes (bash + libnotify)
|
||||||
while :; do tea notifications --all -o simple | xargs -i notify-send {}; sleep 300; done
|
while :; do tea notifications --mine -o simple | xargs -i notify-send {}; sleep 300; done
|
||||||
|
|
||||||
ABOUT
|
ABOUT
|
||||||
Written & maintained by The Gitea Authors.
|
Written & maintained by The Gitea Authors.
|
||||||
|
@ -17,9 +17,26 @@ import (
|
|||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FlagDefaults defines all flags that can be overridden with a default value
|
||||||
|
// via the config file
|
||||||
|
type FlagDefaults struct {
|
||||||
|
// Prefer a specific git remote to use for selecting a repository on gitea,
|
||||||
|
// instead of relying on the remote associated with main/master/trunk branch.
|
||||||
|
// The --remote flag still has precedence over this value.
|
||||||
|
Remote string `yaml:"remote"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preferences that are stored in and read from the config file
|
||||||
|
type Preferences struct {
|
||||||
|
// Prefer using an external text editor over inline multiline prompts
|
||||||
|
Editor bool `yaml:"editor"`
|
||||||
|
FlagDefaults FlagDefaults `yaml:"flag_defaults"`
|
||||||
|
}
|
||||||
|
|
||||||
// LocalConfig represents local configurations
|
// LocalConfig represents local configurations
|
||||||
type LocalConfig struct {
|
type LocalConfig struct {
|
||||||
Logins []Login `yaml:"logins"`
|
Logins []Login `yaml:"logins"`
|
||||||
|
Prefs Preferences `yaml:"preferences"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -55,6 +72,12 @@ func GetConfigPath() string {
|
|||||||
return configFilePath
|
return configFilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPreferences returns preferences based on the config file
|
||||||
|
func GetPreferences() Preferences {
|
||||||
|
loadConfig()
|
||||||
|
return config.Prefs
|
||||||
|
}
|
||||||
|
|
||||||
// loadConfig load config from file
|
// loadConfig load config from file
|
||||||
func loadConfig() (err error) {
|
func loadConfig() (err error) {
|
||||||
loadConfigOnce.Do(func() {
|
loadConfigOnce.Do(func() {
|
||||||
|
@ -111,6 +111,25 @@ func GetLoginByToken(token string) *Login {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLoginByHost finds a login by it's server URL
|
||||||
|
func GetLoginByHost(host string) *Login {
|
||||||
|
err := loadConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range config.Logins {
|
||||||
|
loginURL, err := url.Parse(l.URL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if loginURL.Host == host {
|
||||||
|
return &l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteLogin delete a login by name from config
|
// DeleteLogin delete a login by name from config
|
||||||
func DeleteLogin(name string) error {
|
func DeleteLogin(name string) error {
|
||||||
var idx = -1
|
var idx = -1
|
||||||
@ -142,8 +161,9 @@ func AddLogin(login *Login) error {
|
|||||||
return saveConfig()
|
return saveConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client returns a client to operate Gitea API
|
// Client returns a client to operate Gitea API. You may provide additional modifiers
|
||||||
func (l *Login) Client() *gitea.Client {
|
// for the client like gitea.SetBasicAuth() for customization
|
||||||
|
func (l *Login) Client(options ...gitea.ClientOption) *gitea.Client {
|
||||||
httpClient := &http.Client{}
|
httpClient := &http.Client{}
|
||||||
if l.Insecure {
|
if l.Insecure {
|
||||||
cookieJar, _ := cookiejar.New(nil)
|
cookieJar, _ := cookiejar.New(nil)
|
||||||
@ -155,10 +175,9 @@ func (l *Login) Client() *gitea.Client {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := gitea.NewClient(l.URL,
|
options = append(options, gitea.SetToken(l.Token), gitea.SetHTTPClient(httpClient))
|
||||||
gitea.SetToken(l.Token),
|
|
||||||
gitea.SetHTTPClient(httpClient),
|
client, err := gitea.NewClient(l.URL, options...)
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,9 @@ type TeaContext struct {
|
|||||||
func (ctx *TeaContext) GetListOptions() gitea.ListOptions {
|
func (ctx *TeaContext) GetListOptions() gitea.ListOptions {
|
||||||
page := ctx.Int("page")
|
page := ctx.Int("page")
|
||||||
limit := ctx.Int("limit")
|
limit := ctx.Int("limit")
|
||||||
|
if limit < 0 {
|
||||||
|
limit = 0
|
||||||
|
}
|
||||||
if limit != 0 && page == 0 {
|
if limit != 0 && page == 0 {
|
||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
@ -96,8 +99,12 @@ func InitCommand(ctx *cli.Context) *TeaContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(repoFlag) == 0 || repoFlagPathExists {
|
if len(remoteFlag) == 0 {
|
||||||
// try to read git repo & extract context, ignoring if PWD is not a repo
|
remoteFlag = config.GetPreferences().FlagDefaults.Remote
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to read local git repo & extract context: if repoFlag specifies a valid path, read repo in that dir,
|
||||||
|
// otherwise attempt PWD. if no repo is found, continue with default login
|
||||||
if c.LocalRepo, c.Login, c.RepoSlug, err = contextFromLocalRepo(repoPath, remoteFlag); err != nil {
|
if c.LocalRepo, c.Login, c.RepoSlug, err = contextFromLocalRepo(repoPath, remoteFlag); err != nil {
|
||||||
if err == errNotAGiteaRepo || err == gogit.ErrRepositoryNotExists {
|
if err == errNotAGiteaRepo || err == gogit.ErrRepositoryNotExists {
|
||||||
// we can deal with that, commands needing the optional values use ctx.Ensure()
|
// we can deal with that, commands needing the optional values use ctx.Ensure()
|
||||||
@ -105,7 +112,6 @@ func InitCommand(ctx *cli.Context) *TeaContext {
|
|||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if len(repoFlag) != 0 && !repoFlagPathExists {
|
if len(repoFlag) != 0 && !repoFlagPathExists {
|
||||||
// if repoFlag is not a valid path, use it to override repoSlug
|
// if repoFlag is not a valid path, use it to override repoSlug
|
||||||
@ -120,8 +126,16 @@ func InitCommand(ctx *cli.Context) *TeaContext {
|
|||||||
}
|
}
|
||||||
} else if c.Login == nil {
|
} else if c.Login == nil {
|
||||||
if c.Login, err = config.GetDefaultLogin(); err != nil {
|
if c.Login, err = config.GetDefaultLogin(); err != nil {
|
||||||
log.Fatal(err.Error())
|
if err.Error() == "No available login" {
|
||||||
|
// TODO: maybe we can directly start interact.CreateLogin() (only if
|
||||||
|
// we're sure we can interactively!), as gh cli does.
|
||||||
|
fmt.Println(`No gitea login configured. To start using tea, first run
|
||||||
|
tea login add
|
||||||
|
and then run your command again.`)
|
||||||
}
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("NOTE: no gitea login detected, falling back to login '%s'\n", c.Login.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse reposlug (owner falling back to login owner if reposlug contains only repo name)
|
// parse reposlug (owner falling back to login owner if reposlug contains only repo name)
|
||||||
@ -143,25 +157,39 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
|
|||||||
return repo, nil, "", err
|
return repo, nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no remote
|
|
||||||
if len(gitConfig.Remotes) == 0 {
|
if len(gitConfig.Remotes) == 0 {
|
||||||
return repo, nil, "", errors.New("No remote(s) found in this Git repository")
|
return repo, nil, "", errNotAGiteaRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
// if only one remote exists
|
// When no preferred value is given, choose a remote to find a
|
||||||
if len(gitConfig.Remotes) >= 1 && len(remoteValue) == 0 {
|
// matching login based on its URL.
|
||||||
for remote := range gitConfig.Remotes {
|
if len(gitConfig.Remotes) > 1 && len(remoteValue) == 0 {
|
||||||
remoteValue = remote
|
|
||||||
}
|
|
||||||
if len(gitConfig.Remotes) > 1 {
|
|
||||||
// if master branch is present, use it as the default remote
|
// if master branch is present, use it as the default remote
|
||||||
masterBranch, ok := gitConfig.Branches["master"]
|
mainBranches := []string{"main", "master", "trunk"}
|
||||||
|
for _, b := range mainBranches {
|
||||||
|
masterBranch, ok := gitConfig.Branches[b]
|
||||||
if ok {
|
if ok {
|
||||||
if len(masterBranch.Remote) > 0 {
|
if len(masterBranch.Remote) > 0 {
|
||||||
remoteValue = masterBranch.Remote
|
remoteValue = masterBranch.Remote
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if no branch has matched, default to origin or upstream remote.
|
||||||
|
if len(remoteValue) == 0 {
|
||||||
|
if _, ok := gitConfig.Remotes["upstream"]; ok {
|
||||||
|
remoteValue = "upstream"
|
||||||
|
} else if _, ok := gitConfig.Remotes["origin"]; ok {
|
||||||
|
remoteValue = "origin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make sure a remote is selected
|
||||||
|
if len(remoteValue) == 0 {
|
||||||
|
for remote := range gitConfig.Remotes {
|
||||||
|
remoteValue = remote
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteConfig, ok := gitConfig.Remotes[remoteValue]
|
remoteConfig, ok := gitConfig.Remotes[remoteValue]
|
||||||
@ -174,8 +202,9 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
|
|||||||
return repo, nil, "", err
|
return repo, nil, "", err
|
||||||
}
|
}
|
||||||
for _, l := range logins {
|
for _, l := range logins {
|
||||||
|
sshHost := l.GetSSHHost()
|
||||||
for _, u := range remoteConfig.URLs {
|
for _, u := range remoteConfig.URLs {
|
||||||
p, err := git.ParseURL(strings.TrimSpace(u))
|
p, err := git.ParseURL(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return repo, nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error())
|
return repo, nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error())
|
||||||
}
|
}
|
||||||
@ -186,8 +215,8 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
|
|||||||
return repo, &l, strings.TrimSuffix(path, ".git"), nil
|
return repo, &l, strings.TrimSuffix(path, ".git"), nil
|
||||||
}
|
}
|
||||||
} else if strings.EqualFold(p.Scheme, "ssh") {
|
} else if strings.EqualFold(p.Scheme, "ssh") {
|
||||||
if l.GetSSHHost() == strings.Split(p.Host, ":")[0] {
|
if sshHost == p.Host {
|
||||||
return repo, &l, strings.TrimLeft(strings.TrimSuffix(p.Path, ".git"), "/"), nil
|
return repo, &l, strings.TrimLeft(p.Path, "/"), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ func readSSHPrivKey(keyFile string, passwordCallback pwCallback) (sig ssh.Signer
|
|||||||
}
|
}
|
||||||
sshKey, err := ioutil.ReadFile(keyFile)
|
sshKey, err := ioutil.ReadFile(keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("can not read ssh key '%s'", keyFile)
|
||||||
}
|
}
|
||||||
sig, err = ssh.ParsePrivateKey(sshKey)
|
sig, err = ssh.ParsePrivateKey(sshKey)
|
||||||
if _, ok := err.(*ssh.PassphraseMissingError); ok && passwordCallback != nil {
|
if _, ok := err.(*ssh.PassphraseMissingError); ok && passwordCallback != nil {
|
||||||
|
@ -20,11 +20,20 @@ type URLParser struct {
|
|||||||
|
|
||||||
// Parse parses the git URL
|
// Parse parses the git URL
|
||||||
func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) {
|
func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) {
|
||||||
if !protocolRe.MatchString(rawURL) &&
|
rawURL = strings.TrimSpace(rawURL)
|
||||||
strings.Contains(rawURL, ":") &&
|
|
||||||
|
if !protocolRe.MatchString(rawURL) {
|
||||||
|
// convert the weird git ssh url format to a canonical url:
|
||||||
|
// git@gitea.com:gitea/tea -> ssh://git@gitea.com/gitea/tea
|
||||||
|
if strings.Contains(rawURL, ":") &&
|
||||||
// not a Windows path
|
// not a Windows path
|
||||||
!strings.Contains(rawURL, "\\") {
|
!strings.Contains(rawURL, "\\") {
|
||||||
rawURL = "ssh://" + strings.Replace(rawURL, ":", "/", 1)
|
rawURL = "ssh://" + strings.Replace(rawURL, ":", "/", 1)
|
||||||
|
} else if !strings.Contains(rawURL, "@") &&
|
||||||
|
strings.Count(rawURL, "/") == 2 {
|
||||||
|
// match cases like gitea.com/gitea/tea
|
||||||
|
rawURL = "https://" + rawURL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u, err = url.Parse(rawURL)
|
u, err = url.Parse(rawURL)
|
||||||
|
56
modules/git/url_test.go
Normal file
56
modules/git/url_test.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseUrl(t *testing.T) {
|
||||||
|
u, err := ParseURL("ssh://git@gitea.com:3000/gitea/tea")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "gitea.com:3000", u.Host)
|
||||||
|
assert.Equal(t, "ssh", u.Scheme)
|
||||||
|
assert.Equal(t, "/gitea/tea", u.Path)
|
||||||
|
|
||||||
|
u, err = ParseURL("https://gitea.com/gitea/tea")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "gitea.com", u.Host)
|
||||||
|
assert.Equal(t, "https", u.Scheme)
|
||||||
|
assert.Equal(t, "/gitea/tea", u.Path)
|
||||||
|
|
||||||
|
u, err = ParseURL("git@gitea.com:gitea/tea")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "gitea.com", u.Host)
|
||||||
|
assert.Equal(t, "ssh", u.Scheme)
|
||||||
|
assert.Equal(t, "/gitea/tea", u.Path)
|
||||||
|
|
||||||
|
u, err = ParseURL("gitea.com/gitea/tea")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "gitea.com", u.Host)
|
||||||
|
assert.Equal(t, "https", u.Scheme)
|
||||||
|
assert.Equal(t, "/gitea/tea", u.Path)
|
||||||
|
|
||||||
|
u, err = ParseURL("foo/bar")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "", u.Host)
|
||||||
|
assert.Equal(t, "", u.Scheme)
|
||||||
|
assert.Equal(t, "foo/bar", u.Path)
|
||||||
|
|
||||||
|
u, err = ParseURL("/foo/bar")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "", u.Host)
|
||||||
|
assert.Equal(t, "https", u.Scheme)
|
||||||
|
assert.Equal(t, "/foo/bar", u.Path)
|
||||||
|
|
||||||
|
// this case is unintuitive, but to ambiguous to be handled differently
|
||||||
|
u, err = ParseURL("gitea.com")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "", u.Host)
|
||||||
|
assert.Equal(t, "", u.Scheme)
|
||||||
|
assert.Equal(t, "gitea.com", u.Path)
|
||||||
|
}
|
@ -11,6 +11,7 @@ import (
|
|||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
"code.gitea.io/tea/modules/print"
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
@ -26,7 +27,7 @@ func ShowCommentsMaybeInteractive(ctx *context.TeaContext, idx int64, totalComme
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
print.Comments(comments)
|
print.Comments(comments)
|
||||||
} else if IsInteractive() && !ctx.IsSet("comments") {
|
} else if print.IsInteractive() && !ctx.IsSet("comments") {
|
||||||
// if we're interactive, but --comments hasn't been explicitly set to false
|
// if we're interactive, but --comments hasn't been explicitly set to false
|
||||||
if err := ShowCommentsPaginated(ctx, idx, totalComments); err != nil {
|
if err := ShowCommentsPaginated(ctx, idx, totalComments); err != nil {
|
||||||
fmt.Printf("error while loading comments: %v\n", err)
|
fmt.Printf("error while loading comments: %v\n", err)
|
||||||
@ -69,11 +70,6 @@ func ShowCommentsPaginated(ctx *context.TeaContext, idx int64, totalComments int
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInteractive checks if the output is piped, but NOT if the session is run interactively..
|
|
||||||
func IsInteractive() bool {
|
|
||||||
return terminal.IsTerminal(int(os.Stdout.Fd()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsStdinPiped checks if stdin is piped
|
// IsStdinPiped checks if stdin is piped
|
||||||
func IsStdinPiped() bool {
|
func IsStdinPiped() bool {
|
||||||
return !terminal.IsTerminal(int(os.Stdin.Fd()))
|
return !terminal.IsTerminal(int(os.Stdin.Fd()))
|
||||||
|
@ -43,7 +43,12 @@ func promptIssueProperties(login *config.Login, owner, repo string, o *gitea.Cre
|
|||||||
}
|
}
|
||||||
|
|
||||||
// description
|
// description
|
||||||
promptD := &survey.Multiline{Message: "Issue description:", Default: o.Body}
|
promptD := NewMultiline(Multiline{
|
||||||
|
Message: "Issue description:",
|
||||||
|
Default: o.Body,
|
||||||
|
Syntax: "md",
|
||||||
|
UseEditor: config.GetPreferences().Editor,
|
||||||
|
})
|
||||||
if err = survey.AskOne(promptD, &o.Body); err != nil {
|
if err = survey.AskOne(promptD, &o.Body); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -60,7 +65,7 @@ func promptIssueProperties(login *config.Login, owner, repo string, o *gitea.Cre
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assignees
|
// assignees
|
||||||
if o.Assignees, err = promptMultiSelect("Assignees:", selectables.Collaborators, "[other]"); err != nil {
|
if o.Assignees, err = promptMultiSelect("Assignees:", selectables.Assignees, "[other]"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +99,7 @@ func promptIssueProperties(login *config.Login, owner, repo string, o *gitea.Cre
|
|||||||
|
|
||||||
type issueSelectables struct {
|
type issueSelectables struct {
|
||||||
Repo *gitea.Repository
|
Repo *gitea.Repository
|
||||||
Collaborators []string
|
Assignees []string
|
||||||
MilestoneList []string
|
MilestoneList []string
|
||||||
MilestoneMap map[string]int64
|
MilestoneMap map[string]int64
|
||||||
LabelList []string
|
LabelList []string
|
||||||
@ -119,17 +124,15 @@ func fetchIssueSelectables(login *config.Login, owner, repo string, done chan is
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this should ideally be ListAssignees(), https://github.com/go-gitea/gitea/issues/14856
|
assignees, _, err := c.GetAssignees(owner, repo)
|
||||||
colabs, _, err := c.ListCollaborators(owner, repo, gitea.ListCollaboratorsOptions{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Err = err
|
r.Err = err
|
||||||
done <- r
|
done <- r
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.Collaborators = make([]string, len(colabs)+1)
|
r.Assignees = make([]string, len(assignees))
|
||||||
r.Collaborators[0] = login.User
|
for i, u := range assignees {
|
||||||
for i, u := range colabs {
|
r.Assignees[i] = u.UserName
|
||||||
r.Collaborators[i+1] = u.UserName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
milestones, _, err := c.ListRepoMilestones(owner, repo, gitea.ListMilestoneOption{})
|
milestones, _, err := c.ListRepoMilestones(owner, repo, gitea.ListMilestoneOption{})
|
||||||
@ -145,7 +148,9 @@ func fetchIssueSelectables(login *config.Login, owner, repo string, done chan is
|
|||||||
r.MilestoneList[i] = m.Title
|
r.MilestoneList[i] = m.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
labels, _, err := c.ListRepoLabels(owner, repo, gitea.ListLabelsOptions{})
|
labels, _, err := c.ListRepoLabels(owner, repo, gitea.ListLabelsOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Err = err
|
r.Err = err
|
||||||
done <- r
|
done <- r
|
||||||
|
@ -33,7 +33,11 @@ func CreateMilestone(login *config.Login, owner, repo string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// description
|
// description
|
||||||
promptM := &survey.Multiline{Message: "Milestone description:"}
|
promptM := NewMultiline(Multiline{
|
||||||
|
Message: "Milestone description:",
|
||||||
|
Syntax: "md",
|
||||||
|
UseEditor: config.GetPreferences().Editor,
|
||||||
|
})
|
||||||
if err := survey.AskOne(promptM, &description); err != nil {
|
if err := survey.AskOne(promptM, &description); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,26 @@ import (
|
|||||||
"github.com/araddon/dateparse"
|
"github.com/araddon/dateparse"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PromptMultiline runs a textfield-style prompt and blocks until input was made.
|
// Multiline represents options for a prompt that expects multiline input
|
||||||
func PromptMultiline(message string) (content string, err error) {
|
type Multiline struct {
|
||||||
err = survey.AskOne(&survey.Multiline{Message: message}, &content)
|
Message string
|
||||||
|
Default string
|
||||||
|
Syntax string
|
||||||
|
UseEditor bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMultiline creates a prompt that switches between the inline multiline text
|
||||||
|
// and a texteditor based prompt
|
||||||
|
func NewMultiline(opts Multiline) (prompt survey.Prompt) {
|
||||||
|
if opts.UseEditor {
|
||||||
|
prompt = &survey.Editor{
|
||||||
|
Message: opts.Message,
|
||||||
|
Default: opts.Default,
|
||||||
|
FileName: "*." + opts.Syntax,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prompt = &survey.Multiline{Message: opts.Message, Default: opts.Default}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,26 +6,23 @@ package interact
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
"code.gitea.io/tea/modules/config"
|
"code.gitea.io/tea/modules/context"
|
||||||
"code.gitea.io/tea/modules/git"
|
|
||||||
"code.gitea.io/tea/modules/task"
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreatePull interactively creates a PR
|
// CreatePull interactively creates a PR
|
||||||
func CreatePull(login *config.Login, owner, repo string) error {
|
func CreatePull(ctx *context.TeaContext) (err error) {
|
||||||
var base, head string
|
var base, head string
|
||||||
|
|
||||||
// owner, repo
|
// owner, repo
|
||||||
owner, repo, err := promptRepoSlug(owner, repo)
|
if ctx.Owner, ctx.Repo, err = promptRepoSlug(ctx.Owner, ctx.Repo); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// base
|
// base
|
||||||
base, err = task.GetDefaultPRBase(login, owner, repo)
|
if base, err = task.GetDefaultPRBase(ctx.Login, ctx.Owner, ctx.Repo); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
promptI := &survey.Input{Message: "Target branch:", Default: base}
|
promptI := &survey.Input{Message: "Target branch:", Default: base}
|
||||||
@ -34,15 +31,15 @@ func CreatePull(login *config.Login, owner, repo string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// head
|
// head
|
||||||
localRepo, err := git.RepoForWorkdir()
|
var headOwner, headBranch string
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
promptOpts := survey.WithValidator(survey.Required)
|
promptOpts := survey.WithValidator(survey.Required)
|
||||||
headOwner, headBranch, err := task.GetDefaultPRHead(localRepo)
|
|
||||||
|
if ctx.LocalRepo != nil {
|
||||||
|
headOwner, headBranch, err = task.GetDefaultPRHead(ctx.LocalRepo)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
promptOpts = nil
|
promptOpts = nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
promptI = &survey.Input{Message: "Source repo owner:", Default: headOwner}
|
promptI = &survey.Input{Message: "Source repo owner:", Default: headOwner}
|
||||||
if err := survey.AskOne(promptI, &headOwner); err != nil {
|
if err := survey.AskOne(promptI, &headOwner); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -52,17 +49,15 @@ func CreatePull(login *config.Login, owner, repo string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
head = task.GetHeadSpec(headOwner, headBranch, owner)
|
head = task.GetHeadSpec(headOwner, headBranch, ctx.Owner)
|
||||||
|
|
||||||
opts := gitea.CreateIssueOption{Title: task.GetDefaultPRTitle(head)}
|
opts := gitea.CreateIssueOption{Title: task.GetDefaultPRTitle(head)}
|
||||||
if err = promptIssueProperties(login, owner, repo, &opts); err != nil {
|
if err = promptIssueProperties(ctx.Login, ctx.Owner, ctx.Repo, &opts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return task.CreatePull(
|
return task.CreatePull(
|
||||||
login,
|
ctx,
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
base,
|
base,
|
||||||
head,
|
head,
|
||||||
&opts)
|
&opts)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
"code.gitea.io/tea/modules/task"
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
@ -52,10 +53,14 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error {
|
|||||||
|
|
||||||
// comment
|
// comment
|
||||||
var promptOpts survey.AskOpt
|
var promptOpts survey.AskOpt
|
||||||
if state == gitea.ReviewStateComment || state == gitea.ReviewStateRequestChanges {
|
if (state == gitea.ReviewStateComment && len(codeComments) == 0) || state == gitea.ReviewStateRequestChanges {
|
||||||
promptOpts = survey.WithValidator(survey.Required)
|
promptOpts = survey.WithValidator(survey.Required)
|
||||||
}
|
}
|
||||||
err = survey.AskOne(&survey.Multiline{Message: "Concluding comment:"}, &comment, promptOpts)
|
err = survey.AskOne(NewMultiline(Multiline{
|
||||||
|
Message: "Concluding comment:",
|
||||||
|
Syntax: "md",
|
||||||
|
UseEditor: config.GetPreferences().Editor,
|
||||||
|
}), &comment, promptOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -63,8 +68,9 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error {
|
|||||||
return task.CreatePullReview(ctx, idx, state, comment, codeComments)
|
return task.CreatePullReview(ctx, idx, state, comment, codeComments)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $EDITOR to comment on diff,
|
// DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $VISUAL or $EDITOR to comment on diff,
|
||||||
// (3) parses resulting file into code comments.
|
// (3) parses resulting file into code comments.
|
||||||
|
// It doesn't really make sense to use survey.Editor() here, as we'd read the file content at least twice.
|
||||||
func DoDiffReview(ctx *context.TeaContext, idx int64) ([]gitea.CreatePullReviewComment, error) {
|
func DoDiffReview(ctx *context.TeaContext, idx int64) ([]gitea.CreatePullReviewComment, error) {
|
||||||
tmpFile, err := task.SavePullDiff(ctx, idx)
|
tmpFile, err := task.SavePullDiff(ctx, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
func Comments(comments []*gitea.Comment) {
|
func Comments(comments []*gitea.Comment) {
|
||||||
var baseURL string
|
var baseURL string
|
||||||
if len(comments) != 0 {
|
if len(comments) != 0 {
|
||||||
baseURL = comments[0].HTMLURL
|
baseURL = getRepoURL(comments[0].HTMLURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
var out = make([]string, len(comments))
|
var out = make([]string, len(comments))
|
||||||
@ -32,18 +32,18 @@ func Comments(comments []*gitea.Comment) {
|
|||||||
|
|
||||||
// Comment renders a comment to stdout
|
// Comment renders a comment to stdout
|
||||||
func Comment(c *gitea.Comment) {
|
func Comment(c *gitea.Comment) {
|
||||||
outputMarkdown(formatComment(c), c.HTMLURL)
|
outputMarkdown(formatComment(c), getRepoURL(c.HTMLURL))
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatComment(c *gitea.Comment) string {
|
func formatComment(c *gitea.Comment) string {
|
||||||
edited := ""
|
edited := ""
|
||||||
if c.Updated.After(c.Created) {
|
if c.Updated.After(c.Created) {
|
||||||
edited = fmt.Sprintf(" *(edited on %s)*", FormatTime(c.Updated))
|
edited = fmt.Sprintf(" *(edited on %s)*", FormatTime(c.Updated, false))
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"---\n\n**@%s** wrote on %s%s:\n\n%s\n",
|
"---\n\n**@%s** wrote on %s%s:\n\n%s\n",
|
||||||
c.Poster.UserName,
|
c.Poster.UserName,
|
||||||
FormatTime(c.Created),
|
FormatTime(c.Created, false),
|
||||||
edited,
|
edited,
|
||||||
c.Body,
|
c.Body,
|
||||||
)
|
)
|
||||||
|
@ -6,12 +6,27 @@ package print
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
"github.com/muesli/termenv"
|
"github.com/muesli/termenv"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsInteractive checks if the output is piped, but NOT if the session is run interactively..
|
||||||
|
func IsInteractive() bool {
|
||||||
|
return terminal.IsTerminal(int(os.Stdout.Fd()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// captures the repo URL part <host>/<owner>/<repo> of an url
|
||||||
|
var repoURLRegex = regexp.MustCompile("^([[:alnum:]]+://[^/]+(?:/[[:alnum:]]+){2})/.*")
|
||||||
|
|
||||||
|
func getRepoURL(resourceURL string) string {
|
||||||
|
return repoURLRegex.ReplaceAllString(resourceURL, "$1/")
|
||||||
|
}
|
||||||
|
|
||||||
// formatSize get kb in int and return string
|
// formatSize get kb in int and return string
|
||||||
func formatSize(kb int64) string {
|
func formatSize(kb int64) string {
|
||||||
if kb < 1024 {
|
if kb < 1024 {
|
||||||
@ -28,8 +43,18 @@ func formatSize(kb int64) string {
|
|||||||
return fmt.Sprintf("%d Tb", gb/1024)
|
return fmt.Sprintf("%d Tb", gb/1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatTime give a date-time in local timezone if available
|
// FormatTime provides a string for the given time value.
|
||||||
func FormatTime(t time.Time) string {
|
// If machineReadable is set, a UTC RFC3339 string is returned,
|
||||||
|
// otherwise a simplified string in local time is used.
|
||||||
|
func FormatTime(t time.Time, machineReadable bool) string {
|
||||||
|
if t.IsZero() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if machineReadable {
|
||||||
|
return t.UTC().Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
location, err := time.LoadLocation("Local")
|
location, err := time.LoadLocation("Local")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return t.Format("2006-01-02 15:04 UTC")
|
return t.Format("2006-01-02 15:04 UTC")
|
||||||
@ -72,3 +97,16 @@ func formatUserName(u *gitea.User) string {
|
|||||||
}
|
}
|
||||||
return u.FullName
|
return u.FullName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatBoolean(b bool, allowIcons bool) string {
|
||||||
|
if !allowIcons {
|
||||||
|
return fmt.Sprintf("%v", b)
|
||||||
|
}
|
||||||
|
|
||||||
|
styled := "✔"
|
||||||
|
if !b {
|
||||||
|
styled = "✖"
|
||||||
|
}
|
||||||
|
|
||||||
|
return styled
|
||||||
|
}
|
||||||
|
@ -9,19 +9,40 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/enescakir/emoji"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IssueDetails print an issue rendered to stdout
|
// IssueDetails print an issue rendered to stdout
|
||||||
func IssueDetails(issue *gitea.Issue) {
|
func IssueDetails(issue *gitea.Issue, reactions []*gitea.Reaction) {
|
||||||
outputMarkdown(fmt.Sprintf(
|
out := fmt.Sprintf(
|
||||||
"# #%d %s (%s)\n@%s created %s\n\n%s\n",
|
"# #%d %s (%s)\n@%s created %s\n\n%s\n",
|
||||||
issue.Index,
|
issue.Index,
|
||||||
issue.Title,
|
issue.Title,
|
||||||
issue.State,
|
issue.State,
|
||||||
issue.Poster.UserName,
|
issue.Poster.UserName,
|
||||||
FormatTime(issue.Created),
|
FormatTime(issue.Created, false),
|
||||||
issue.Body,
|
issue.Body,
|
||||||
), issue.HTMLURL)
|
)
|
||||||
|
|
||||||
|
if len(reactions) > 0 {
|
||||||
|
out += fmt.Sprintf("\n---\n\n%s\n", formatReactions(reactions))
|
||||||
|
}
|
||||||
|
|
||||||
|
outputMarkdown(out, getRepoURL(issue.HTMLURL))
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatReactions(reactions []*gitea.Reaction) string {
|
||||||
|
reactionCounts := make(map[string]uint16)
|
||||||
|
for _, r := range reactions {
|
||||||
|
reactionCounts[r.Reaction]++
|
||||||
|
}
|
||||||
|
|
||||||
|
reactionStrings := make([]string, 0, len(reactionCounts))
|
||||||
|
for reaction, count := range reactionCounts {
|
||||||
|
reactionStrings = append(reactionStrings, fmt.Sprintf("%dx :%s:", count, reaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
return emoji.Parse(strings.Join(reactionStrings, " | "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssuesPullsList prints a listing of issues & pulls
|
// IssuesPullsList prints a listing of issues & pulls
|
||||||
@ -54,19 +75,20 @@ var IssueFields = []string{
|
|||||||
func printIssues(issues []*gitea.Issue, output string, fields []string) {
|
func printIssues(issues []*gitea.Issue, output string, fields []string) {
|
||||||
labelMap := map[int64]string{}
|
labelMap := map[int64]string{}
|
||||||
var printables = make([]printable, len(issues))
|
var printables = make([]printable, len(issues))
|
||||||
|
machineReadable := isMachineReadable(output)
|
||||||
|
|
||||||
for i, x := range issues {
|
for i, x := range issues {
|
||||||
// pre-serialize labels for performance
|
// pre-serialize labels for performance
|
||||||
for _, label := range x.Labels {
|
for _, label := range x.Labels {
|
||||||
if _, ok := labelMap[label.ID]; !ok {
|
if _, ok := labelMap[label.ID]; !ok {
|
||||||
labelMap[label.ID] = formatLabel(label, !isMachineReadable(output), "")
|
labelMap[label.ID] = formatLabel(label, !machineReadable, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// store items with printable interface
|
// store items with printable interface
|
||||||
printables[i] = &printableIssue{x, &labelMap}
|
printables[i] = &printableIssue{x, &labelMap}
|
||||||
}
|
}
|
||||||
|
|
||||||
t := tableFromItems(fields, printables)
|
t := tableFromItems(fields, printables, machineReadable)
|
||||||
t.print(output)
|
t.print(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +97,7 @@ type printableIssue struct {
|
|||||||
formattedLabels *map[int64]string
|
formattedLabels *map[int64]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x printableIssue) FormatField(field string) string {
|
func (x printableIssue) FormatField(field string, machineReadable bool) string {
|
||||||
switch field {
|
switch field {
|
||||||
case "index":
|
case "index":
|
||||||
return fmt.Sprintf("%d", x.Index)
|
return fmt.Sprintf("%d", x.Index)
|
||||||
@ -97,11 +119,14 @@ func (x printableIssue) FormatField(field string) string {
|
|||||||
case "body":
|
case "body":
|
||||||
return x.Body
|
return x.Body
|
||||||
case "created":
|
case "created":
|
||||||
return FormatTime(x.Created)
|
return FormatTime(x.Created, machineReadable)
|
||||||
case "updated":
|
case "updated":
|
||||||
return FormatTime(x.Updated)
|
return FormatTime(x.Updated, machineReadable)
|
||||||
case "deadline":
|
case "deadline":
|
||||||
return FormatTime(*x.Deadline)
|
if x.Deadline == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return FormatTime(*x.Deadline, machineReadable)
|
||||||
case "milestone":
|
case "milestone":
|
||||||
if x.Milestone != nil {
|
if x.Milestone != nil {
|
||||||
return x.Milestone.Title
|
return x.Milestone.Title
|
||||||
|
@ -16,9 +16,17 @@ import (
|
|||||||
// If the input could not be parsed, it is printed unformatted, the error
|
// If the input could not be parsed, it is printed unformatted, the error
|
||||||
// is returned anyway.
|
// is returned anyway.
|
||||||
func outputMarkdown(markdown string, baseURL string) error {
|
func outputMarkdown(markdown string, baseURL string) error {
|
||||||
|
var styleOption glamour.TermRendererOption
|
||||||
|
if IsInteractive() {
|
||||||
|
styleOption = glamour.WithAutoStyle()
|
||||||
|
} else {
|
||||||
|
styleOption = glamour.WithStandardStyle("notty")
|
||||||
|
}
|
||||||
|
|
||||||
renderer, err := glamour.NewTermRenderer(
|
renderer, err := glamour.NewTermRenderer(
|
||||||
glamour.WithAutoStyle(),
|
styleOption,
|
||||||
glamour.WithBaseURL(baseURL),
|
glamour.WithBaseURL(baseURL),
|
||||||
|
glamour.WithPreservedNewLines(),
|
||||||
glamour.WithWordWrap(getWordWrap()),
|
glamour.WithWordWrap(getWordWrap()),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -19,45 +19,70 @@ func MilestoneDetails(milestone *gitea.Milestone) {
|
|||||||
fmt.Printf("\n%s\n", milestone.Description)
|
fmt.Printf("\n%s\n", milestone.Description)
|
||||||
}
|
}
|
||||||
if milestone.Deadline != nil && !milestone.Deadline.IsZero() {
|
if milestone.Deadline != nil && !milestone.Deadline.IsZero() {
|
||||||
fmt.Printf("\nDeadline: %s\n", FormatTime(*milestone.Deadline))
|
fmt.Printf("\nDeadline: %s\n", FormatTime(*milestone.Deadline, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MilestonesList prints a listing of milestones
|
// MilestonesList prints a listing of milestones
|
||||||
func MilestonesList(miles []*gitea.Milestone, output string, state gitea.StateType) {
|
func MilestonesList(news []*gitea.Milestone, output string, fields []string) {
|
||||||
headers := []string{
|
var printables = make([]printable, len(news))
|
||||||
"Title",
|
for i, x := range news {
|
||||||
|
printables[i] = &printableMilestone{x}
|
||||||
}
|
}
|
||||||
if state == gitea.StateAll {
|
t := tableFromItems(fields, printables, isMachineReadable(output))
|
||||||
headers = append(headers, "State")
|
|
||||||
}
|
|
||||||
headers = append(headers,
|
|
||||||
"Open/Closed Issues",
|
|
||||||
"DueDate",
|
|
||||||
)
|
|
||||||
|
|
||||||
t := table{headers: headers}
|
|
||||||
|
|
||||||
for _, m := range miles {
|
|
||||||
var deadline = ""
|
|
||||||
|
|
||||||
if m.Deadline != nil && !m.Deadline.IsZero() {
|
|
||||||
deadline = FormatTime(*m.Deadline)
|
|
||||||
}
|
|
||||||
|
|
||||||
item := []string{
|
|
||||||
m.Title,
|
|
||||||
}
|
|
||||||
if state == gitea.StateAll {
|
|
||||||
item = append(item, string(m.State))
|
|
||||||
}
|
|
||||||
item = append(item,
|
|
||||||
fmt.Sprintf("%d/%d", m.OpenIssues, m.ClosedIssues),
|
|
||||||
deadline,
|
|
||||||
)
|
|
||||||
t.addRowSlice(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.sort(0, true)
|
t.sort(0, true)
|
||||||
t.print(output)
|
t.print(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MilestoneFields are all available fields to print with MilestonesList
|
||||||
|
var MilestoneFields = []string{
|
||||||
|
"title",
|
||||||
|
"state",
|
||||||
|
"items_open",
|
||||||
|
"items_closed",
|
||||||
|
"items",
|
||||||
|
"duedate",
|
||||||
|
"description",
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
"closed",
|
||||||
|
"id",
|
||||||
|
}
|
||||||
|
|
||||||
|
type printableMilestone struct {
|
||||||
|
*gitea.Milestone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m printableMilestone) FormatField(field string, machineReadable bool) string {
|
||||||
|
switch field {
|
||||||
|
case "title":
|
||||||
|
return m.Title
|
||||||
|
case "state":
|
||||||
|
return string(m.State)
|
||||||
|
case "items_open":
|
||||||
|
return fmt.Sprintf("%d", m.OpenIssues)
|
||||||
|
case "items_closed":
|
||||||
|
return fmt.Sprintf("%d", m.ClosedIssues)
|
||||||
|
case "items":
|
||||||
|
return fmt.Sprintf("%d/%d", m.OpenIssues, m.ClosedIssues)
|
||||||
|
case "duedate":
|
||||||
|
if m.Deadline != nil && !m.Deadline.IsZero() {
|
||||||
|
return FormatTime(*m.Deadline, machineReadable)
|
||||||
|
}
|
||||||
|
case "id":
|
||||||
|
return fmt.Sprintf("%d", m.ID)
|
||||||
|
case "description":
|
||||||
|
return m.Description
|
||||||
|
case "created":
|
||||||
|
return FormatTime(m.Created, machineReadable)
|
||||||
|
case "updated":
|
||||||
|
if m.Updated != nil {
|
||||||
|
return FormatTime(*m.Updated, machineReadable)
|
||||||
|
}
|
||||||
|
case "closed":
|
||||||
|
if m.Closed != nil {
|
||||||
|
return FormatTime(*m.Closed, machineReadable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
@ -5,29 +5,58 @@
|
|||||||
package print
|
package print
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NotificationsList prints a listing of notification threads
|
// NotificationsList prints a listing of notification threads
|
||||||
func NotificationsList(news []*gitea.NotificationThread, output string, showRepository bool) {
|
func NotificationsList(news []*gitea.NotificationThread, output string, fields []string) {
|
||||||
headers := []string{
|
var printables = make([]printable, len(news))
|
||||||
"Type",
|
for i, x := range news {
|
||||||
"Index",
|
printables[i] = &printableNotification{x}
|
||||||
"Title",
|
|
||||||
}
|
}
|
||||||
if showRepository {
|
t := tableFromItems(fields, printables, isMachineReadable(output))
|
||||||
headers = append(headers, "Repository")
|
t.print(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
t := table{headers: headers}
|
// NotificationFields are all available fields to print with NotificationsList
|
||||||
|
var NotificationFields = []string{
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"updated",
|
||||||
|
|
||||||
for _, n := range news {
|
// these are about the notification subject
|
||||||
if n.Subject == nil {
|
"index",
|
||||||
continue
|
"type",
|
||||||
|
"state",
|
||||||
|
"title",
|
||||||
|
"repository",
|
||||||
}
|
}
|
||||||
// if pull or Issue get Index
|
|
||||||
|
type printableNotification struct {
|
||||||
|
*gitea.NotificationThread
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n printableNotification) FormatField(field string, machineReadable bool) string {
|
||||||
|
switch field {
|
||||||
|
case "id":
|
||||||
|
return fmt.Sprintf("%d", n.ID)
|
||||||
|
|
||||||
|
case "status":
|
||||||
|
status := "read"
|
||||||
|
if n.Pinned {
|
||||||
|
status = "pinned"
|
||||||
|
} else if n.Unread {
|
||||||
|
status = "unread"
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
|
||||||
|
case "updated":
|
||||||
|
return FormatTime(n.UpdatedAt, machineReadable)
|
||||||
|
|
||||||
|
case "index":
|
||||||
var index string
|
var index string
|
||||||
if n.Subject.Type == "Issue" || n.Subject.Type == "Pull" {
|
if n.Subject.Type == "Issue" || n.Subject.Type == "Pull" {
|
||||||
index = n.Subject.URL
|
index = n.Subject.URL
|
||||||
@ -35,17 +64,20 @@ func NotificationsList(news []*gitea.NotificationThread, output string, showRepo
|
|||||||
if len(urlParts) != 0 {
|
if len(urlParts) != 0 {
|
||||||
index = urlParts[len(urlParts)-1]
|
index = urlParts[len(urlParts)-1]
|
||||||
}
|
}
|
||||||
index = "#" + index
|
|
||||||
}
|
}
|
||||||
|
return index
|
||||||
|
|
||||||
item := []string{n.Subject.Type, index, n.Subject.Title}
|
case "type":
|
||||||
if showRepository {
|
return string(n.Subject.Type)
|
||||||
item = append(item, n.Repository.FullName)
|
|
||||||
}
|
|
||||||
t.addRowSlice(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Len() != 0 {
|
case "state":
|
||||||
t.print(output)
|
return string(n.Subject.State)
|
||||||
|
|
||||||
|
case "title":
|
||||||
|
return n.Subject.Title
|
||||||
|
|
||||||
|
case "repo", "repository":
|
||||||
|
return n.Repository.FullName
|
||||||
}
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,18 @@ import (
|
|||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// OrganizationDetails prints details of an org with formatting
|
||||||
|
func OrganizationDetails(org *gitea.Organization) {
|
||||||
|
outputMarkdown(fmt.Sprintf(
|
||||||
|
"# %s\n%s\n\n- Visibility: %s\n- Location: %s\n- Website: %s\n",
|
||||||
|
org.UserName,
|
||||||
|
org.Description,
|
||||||
|
org.Visibility,
|
||||||
|
org.Location,
|
||||||
|
org.Website,
|
||||||
|
), "")
|
||||||
|
}
|
||||||
|
|
||||||
// OrganizationsList prints a listing of the organizations
|
// OrganizationsList prints a listing of the organizations
|
||||||
func OrganizationsList(organizations []*gitea.Organization, output string) {
|
func OrganizationsList(organizations []*gitea.Organization, output string) {
|
||||||
if len(organizations) == 0 {
|
if len(organizations) == 0 {
|
||||||
|
@ -6,7 +6,6 @@ package print
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
@ -23,19 +22,8 @@ var ciStatusSymbols = map[gitea.StatusState]string{
|
|||||||
// PullDetails print an pull rendered to stdout
|
// PullDetails print an pull rendered to stdout
|
||||||
func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *gitea.CombinedStatus) {
|
func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *gitea.CombinedStatus) {
|
||||||
base := pr.Base.Name
|
base := pr.Base.Name
|
||||||
head := pr.Head.Name
|
head := formatPRHead(pr)
|
||||||
if pr.Head.RepoID != pr.Base.RepoID {
|
state := formatPRState(pr)
|
||||||
if pr.Head.Repository != nil {
|
|
||||||
head = pr.Head.Repository.Owner.UserName + ":" + head
|
|
||||||
} else {
|
|
||||||
head = "delete:" + head
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state := pr.State
|
|
||||||
if pr.Merged != nil {
|
|
||||||
state = "merged"
|
|
||||||
}
|
|
||||||
|
|
||||||
out := fmt.Sprintf(
|
out := fmt.Sprintf(
|
||||||
"# #%d %s (%s)\n@%s created %s\t**%s** <- **%s**\n\n%s\n\n",
|
"# #%d %s (%s)\n@%s created %s\t**%s** <- **%s**\n\n%s\n\n",
|
||||||
@ -43,7 +31,7 @@ func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *g
|
|||||||
pr.Title,
|
pr.Title,
|
||||||
state,
|
state,
|
||||||
pr.Poster.UserName,
|
pr.Poster.UserName,
|
||||||
FormatTime(*pr.Created),
|
FormatTime(*pr.Created, false),
|
||||||
base,
|
base,
|
||||||
head,
|
head,
|
||||||
pr.Body,
|
pr.Body,
|
||||||
@ -76,7 +64,26 @@ func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *g
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outputMarkdown(out, pr.HTMLURL)
|
outputMarkdown(out, getRepoURL(pr.HTMLURL))
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPRHead(pr *gitea.PullRequest) string {
|
||||||
|
head := pr.Head.Name
|
||||||
|
if pr.Head.RepoID != pr.Base.RepoID {
|
||||||
|
if pr.Head.Repository != nil {
|
||||||
|
head = pr.Head.Repository.Owner.UserName + ":" + head
|
||||||
|
} else {
|
||||||
|
head = "delete:" + head
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return head
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPRState(pr *gitea.PullRequest) string {
|
||||||
|
if pr.Merged != nil {
|
||||||
|
return "merged"
|
||||||
|
}
|
||||||
|
return string(pr.State)
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatReviews(reviews []*gitea.PullReview) string {
|
func formatReviews(reviews []*gitea.PullReview) string {
|
||||||
@ -92,11 +99,14 @@ func formatReviews(reviews []*gitea.PullReview) string {
|
|||||||
case gitea.ReviewStateApproved,
|
case gitea.ReviewStateApproved,
|
||||||
gitea.ReviewStateRequestChanges,
|
gitea.ReviewStateRequestChanges,
|
||||||
gitea.ReviewStateRequestReview:
|
gitea.ReviewStateRequestReview:
|
||||||
|
// only user reviews are supported no team review requests
|
||||||
|
if review.Reviewer != nil {
|
||||||
if r, ok := reviewByUser[review.Reviewer.ID]; !ok || review.Submitted.After(r.Submitted) {
|
if r, ok := reviewByUser[review.Reviewer.ID]; !ok || review.Submitted.After(r.Submitted) {
|
||||||
reviewByUser[review.Reviewer.ID] = review
|
reviewByUser[review.Reviewer.ID] = review
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// group reviews by type
|
// group reviews by type
|
||||||
usersByState := make(map[gitea.ReviewStateType][]string)
|
usersByState := make(map[gitea.ReviewStateType][]string)
|
||||||
@ -114,37 +124,120 @@ func formatReviews(reviews []*gitea.PullReview) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PullsList prints a listing of pulls
|
// PullsList prints a listing of pulls
|
||||||
func PullsList(prs []*gitea.PullRequest, output string) {
|
func PullsList(prs []*gitea.PullRequest, output string, fields []string) {
|
||||||
t := tableWithHeader(
|
printPulls(prs, output, fields)
|
||||||
"Index",
|
|
||||||
"Title",
|
|
||||||
"State",
|
|
||||||
"Author",
|
|
||||||
"Milestone",
|
|
||||||
"Updated",
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, pr := range prs {
|
|
||||||
if pr == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
author := pr.Poster.FullName
|
|
||||||
if len(author) == 0 {
|
|
||||||
author = pr.Poster.UserName
|
|
||||||
}
|
|
||||||
mile := ""
|
|
||||||
if pr.Milestone != nil {
|
|
||||||
mile = pr.Milestone.Title
|
|
||||||
}
|
|
||||||
t.addRow(
|
|
||||||
strconv.FormatInt(pr.Index, 10),
|
|
||||||
pr.Title,
|
|
||||||
string(pr.State),
|
|
||||||
author,
|
|
||||||
mile,
|
|
||||||
FormatTime(*pr.Updated),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PullFields are all available fields to print with PullsList()
|
||||||
|
var PullFields = []string{
|
||||||
|
"index",
|
||||||
|
"state",
|
||||||
|
"author",
|
||||||
|
"author-id",
|
||||||
|
"url",
|
||||||
|
|
||||||
|
"title",
|
||||||
|
"body",
|
||||||
|
|
||||||
|
"mergeable",
|
||||||
|
"base",
|
||||||
|
"base-commit",
|
||||||
|
"head",
|
||||||
|
"diff",
|
||||||
|
"patch",
|
||||||
|
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
"deadline",
|
||||||
|
|
||||||
|
"assignees",
|
||||||
|
"milestone",
|
||||||
|
"labels",
|
||||||
|
"comments",
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPulls(pulls []*gitea.PullRequest, output string, fields []string) {
|
||||||
|
labelMap := map[int64]string{}
|
||||||
|
var printables = make([]printable, len(pulls))
|
||||||
|
machineReadable := isMachineReadable(output)
|
||||||
|
|
||||||
|
for i, x := range pulls {
|
||||||
|
// pre-serialize labels for performance
|
||||||
|
for _, label := range x.Labels {
|
||||||
|
if _, ok := labelMap[label.ID]; !ok {
|
||||||
|
labelMap[label.ID] = formatLabel(label, !machineReadable, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// store items with printable interface
|
||||||
|
printables[i] = &printablePull{x, &labelMap}
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tableFromItems(fields, printables, machineReadable)
|
||||||
t.print(output)
|
t.print(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type printablePull struct {
|
||||||
|
*gitea.PullRequest
|
||||||
|
formattedLabels *map[int64]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x printablePull) FormatField(field string, machineReadable bool) string {
|
||||||
|
switch field {
|
||||||
|
case "index":
|
||||||
|
return fmt.Sprintf("%d", x.Index)
|
||||||
|
case "state":
|
||||||
|
return formatPRState(x.PullRequest)
|
||||||
|
case "author":
|
||||||
|
return formatUserName(x.Poster)
|
||||||
|
case "author-id":
|
||||||
|
return x.Poster.UserName
|
||||||
|
case "url":
|
||||||
|
return x.HTMLURL
|
||||||
|
case "title":
|
||||||
|
return x.Title
|
||||||
|
case "body":
|
||||||
|
return x.Body
|
||||||
|
case "created":
|
||||||
|
return FormatTime(*x.Created, machineReadable)
|
||||||
|
case "updated":
|
||||||
|
return FormatTime(*x.Updated, machineReadable)
|
||||||
|
case "deadline":
|
||||||
|
if x.Deadline == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return FormatTime(*x.Deadline, machineReadable)
|
||||||
|
case "milestone":
|
||||||
|
if x.Milestone != nil {
|
||||||
|
return x.Milestone.Title
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
case "labels":
|
||||||
|
var labels = make([]string, len(x.Labels))
|
||||||
|
for i, l := range x.Labels {
|
||||||
|
labels[i] = (*x.formattedLabels)[l.ID]
|
||||||
|
}
|
||||||
|
return strings.Join(labels, " ")
|
||||||
|
case "assignees":
|
||||||
|
var assignees = make([]string, len(x.Assignees))
|
||||||
|
for i, a := range x.Assignees {
|
||||||
|
assignees[i] = formatUserName(a)
|
||||||
|
}
|
||||||
|
return strings.Join(assignees, " ")
|
||||||
|
case "comments":
|
||||||
|
return fmt.Sprintf("%d", x.Comments)
|
||||||
|
case "mergeable":
|
||||||
|
isMergeable := x.Mergeable && x.State == gitea.StateOpen
|
||||||
|
return formatBoolean(isMergeable, !machineReadable)
|
||||||
|
case "base":
|
||||||
|
return x.Base.Ref
|
||||||
|
case "base-commit":
|
||||||
|
return x.MergeBase
|
||||||
|
case "head":
|
||||||
|
return formatPRHead(x.PullRequest)
|
||||||
|
case "diff":
|
||||||
|
return x.DiffURL
|
||||||
|
case "patch":
|
||||||
|
return x.PatchURL
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@ func ReleasesList(releases []*gitea.Release, output string) {
|
|||||||
t.addRow(
|
t.addRow(
|
||||||
release.TagName,
|
release.TagName,
|
||||||
release.Title,
|
release.Title,
|
||||||
FormatTime(release.PublishedAt),
|
FormatTime(release.PublishedAt, isMachineReadable(output)),
|
||||||
status,
|
status,
|
||||||
release.TarURL,
|
release.TarURL,
|
||||||
)
|
)
|
||||||
|
@ -18,7 +18,7 @@ func ReposList(repos []*gitea.Repository, output string, fields []string) {
|
|||||||
for i, r := range repos {
|
for i, r := range repos {
|
||||||
printables[i] = &printableRepo{r}
|
printables[i] = &printableRepo{r}
|
||||||
}
|
}
|
||||||
t := tableFromItems(fields, printables)
|
t := tableFromItems(fields, printables, isMachineReadable(output))
|
||||||
t.print(output)
|
t.print(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ var RepoFields = []string{
|
|||||||
|
|
||||||
type printableRepo struct{ *gitea.Repository }
|
type printableRepo struct{ *gitea.Repository }
|
||||||
|
|
||||||
func (x printableRepo) FormatField(field string) string {
|
func (x printableRepo) FormatField(field string, machineReadable bool) string {
|
||||||
switch field {
|
switch field {
|
||||||
case "description":
|
case "description":
|
||||||
return x.Description
|
return x.Description
|
||||||
@ -124,7 +124,7 @@ func (x printableRepo) FormatField(field string) string {
|
|||||||
case "ssh":
|
case "ssh":
|
||||||
return x.SSHURL
|
return x.SSHURL
|
||||||
case "updated":
|
case "updated":
|
||||||
return FormatTime(x.Updated)
|
return FormatTime(x.Updated, machineReadable)
|
||||||
case "url":
|
case "url":
|
||||||
return x.HTMLURL
|
return x.HTMLURL
|
||||||
case "permission":
|
case "permission":
|
||||||
|
@ -24,16 +24,16 @@ type table struct {
|
|||||||
|
|
||||||
// printable can be implemented for structs to put fields dynamically into a table
|
// printable can be implemented for structs to put fields dynamically into a table
|
||||||
type printable interface {
|
type printable interface {
|
||||||
FormatField(field string) string
|
FormatField(field string, machineReadable bool) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// high level api to print a table of items with dynamic fields
|
// high level api to print a table of items with dynamic fields
|
||||||
func tableFromItems(fields []string, values []printable) table {
|
func tableFromItems(fields []string, values []printable, machineReadable bool) table {
|
||||||
t := table{headers: fields}
|
t := table{headers: fields}
|
||||||
for _, v := range values {
|
for _, v := range values {
|
||||||
row := make([]string, len(fields))
|
row := make([]string, len(fields))
|
||||||
for i, f := range fields {
|
for i, f := range fields {
|
||||||
row[i] = v.FormatField(f)
|
row[i] = v.FormatField(f, machineReadable)
|
||||||
}
|
}
|
||||||
t.addRowSlice(row)
|
t.addRowSlice(row)
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ func outputyaml(headers []string, values [][]string) {
|
|||||||
|
|
||||||
func isMachineReadable(outputFormat string) bool {
|
func isMachineReadable(outputFormat string) bool {
|
||||||
switch outputFormat {
|
switch outputFormat {
|
||||||
case "yml", "yaml", "csv":
|
case "yml", "yaml", "csv", "tsv":
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -18,7 +18,7 @@ func TrackedTimesList(times []*gitea.TrackedTime, outputType string, fields []st
|
|||||||
totalDuration += t.Time
|
totalDuration += t.Time
|
||||||
printables[i] = &printableTrackedTime{t, outputType}
|
printables[i] = &printableTrackedTime{t, outputType}
|
||||||
}
|
}
|
||||||
t := tableFromItems(fields, printables)
|
t := tableFromItems(fields, printables, isMachineReadable(outputType))
|
||||||
|
|
||||||
if printTotal {
|
if printTotal {
|
||||||
total := make([]string, len(fields))
|
total := make([]string, len(fields))
|
||||||
@ -45,12 +45,12 @@ type printableTrackedTime struct {
|
|||||||
outputFormat string
|
outputFormat string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t printableTrackedTime) FormatField(field string) string {
|
func (t printableTrackedTime) FormatField(field string, machineReadable bool) string {
|
||||||
switch field {
|
switch field {
|
||||||
case "id":
|
case "id":
|
||||||
return fmt.Sprintf("%d", t.ID)
|
return fmt.Sprintf("%d", t.ID)
|
||||||
case "created":
|
case "created":
|
||||||
return FormatTime(t.Created)
|
return FormatTime(t.Created, machineReadable)
|
||||||
case "repo":
|
case "repo":
|
||||||
return t.Issue.Repository.FullName
|
return t.Issue.Repository.FullName
|
||||||
case "issue":
|
case "issue":
|
||||||
|
135
modules/print/user.go
Normal file
135
modules/print/user.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package print
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserDetails print a formatted user to stdout
|
||||||
|
func UserDetails(user *gitea.User) {
|
||||||
|
title := "# " + user.UserName
|
||||||
|
if user.IsAdmin {
|
||||||
|
title += " (admin)"
|
||||||
|
}
|
||||||
|
if !user.IsActive {
|
||||||
|
title += " (disabled)"
|
||||||
|
}
|
||||||
|
if user.Restricted {
|
||||||
|
title += " (restricted)"
|
||||||
|
}
|
||||||
|
if user.ProhibitLogin {
|
||||||
|
title += " (login prohibited)"
|
||||||
|
}
|
||||||
|
title += "\n"
|
||||||
|
|
||||||
|
var desc string
|
||||||
|
if len(user.Description) != 0 {
|
||||||
|
desc = fmt.Sprintf("*%s*\n\n", user.Description)
|
||||||
|
}
|
||||||
|
var website string
|
||||||
|
if len(user.Website) != 0 {
|
||||||
|
website = fmt.Sprintf("%s\n\n", user.Website)
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := fmt.Sprintf(
|
||||||
|
"Follower Count: %d, Following Count: %d, Starred Repos: %d\n",
|
||||||
|
user.FollowerCount,
|
||||||
|
user.FollowingCount,
|
||||||
|
user.StarredRepoCount,
|
||||||
|
)
|
||||||
|
|
||||||
|
outputMarkdown(fmt.Sprintf(
|
||||||
|
"%s%s\n%s\n%s",
|
||||||
|
title,
|
||||||
|
desc,
|
||||||
|
website,
|
||||||
|
stats,
|
||||||
|
), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserList prints a listing of the users
|
||||||
|
func UserList(user []*gitea.User, output string, fields []string) {
|
||||||
|
var printables = make([]printable, len(user))
|
||||||
|
for i, u := range user {
|
||||||
|
printables[i] = &printableUser{u}
|
||||||
|
}
|
||||||
|
t := tableFromItems(fields, printables, isMachineReadable(output))
|
||||||
|
t.print(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserFields are the available fields to print with UserList()
|
||||||
|
var UserFields = []string{
|
||||||
|
"id",
|
||||||
|
"login",
|
||||||
|
"full_name",
|
||||||
|
"email",
|
||||||
|
"avatar_url",
|
||||||
|
"language",
|
||||||
|
"is_admin",
|
||||||
|
"restricted",
|
||||||
|
"prohibit_login",
|
||||||
|
"location",
|
||||||
|
"website",
|
||||||
|
"description",
|
||||||
|
"visibility",
|
||||||
|
"activated",
|
||||||
|
"lastlogin_at",
|
||||||
|
"created_at",
|
||||||
|
}
|
||||||
|
|
||||||
|
type printableUser struct{ *gitea.User }
|
||||||
|
|
||||||
|
func (x printableUser) FormatField(field string, machineReadable bool) string {
|
||||||
|
switch field {
|
||||||
|
case "id":
|
||||||
|
return fmt.Sprintf("%d", x.ID)
|
||||||
|
case "login":
|
||||||
|
if x.IsAdmin {
|
||||||
|
return fmt.Sprintf("%s (admin)", x.UserName)
|
||||||
|
}
|
||||||
|
if !x.IsActive {
|
||||||
|
return fmt.Sprintf("%s (disabled)", x.UserName)
|
||||||
|
}
|
||||||
|
if x.Restricted {
|
||||||
|
return fmt.Sprintf("%s (restricted)", x.UserName)
|
||||||
|
}
|
||||||
|
if x.ProhibitLogin {
|
||||||
|
return fmt.Sprintf("%s (login prohibited)", x.UserName)
|
||||||
|
}
|
||||||
|
return x.UserName
|
||||||
|
case "full_name":
|
||||||
|
return x.FullName
|
||||||
|
case "email":
|
||||||
|
return x.Email
|
||||||
|
case "avatar_url":
|
||||||
|
return x.AvatarURL
|
||||||
|
case "language":
|
||||||
|
return x.Language
|
||||||
|
case "is_admin":
|
||||||
|
return formatBoolean(x.IsAdmin, !machineReadable)
|
||||||
|
case "restricted":
|
||||||
|
return formatBoolean(x.Restricted, !machineReadable)
|
||||||
|
case "prohibit_login":
|
||||||
|
return formatBoolean(x.ProhibitLogin, !machineReadable)
|
||||||
|
case "activated":
|
||||||
|
return formatBoolean(x.IsActive, !machineReadable)
|
||||||
|
case "location":
|
||||||
|
return x.Location
|
||||||
|
case "website":
|
||||||
|
return x.Website
|
||||||
|
case "description":
|
||||||
|
return x.Description
|
||||||
|
case "visibility":
|
||||||
|
return string(x.Visibility)
|
||||||
|
case "created_at":
|
||||||
|
return FormatTime(x.Created, machineReadable)
|
||||||
|
case "lastlogin_at":
|
||||||
|
return FormatTime(x.LastLogin, machineReadable)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
@ -25,7 +25,7 @@ func CreateIssue(login *config.Login, repoOwner, repoName string, opts gitea.Cre
|
|||||||
return fmt.Errorf("could not create issue: %s", err)
|
return fmt.Errorf("could not create issue: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
print.IssueDetails(issue)
|
print.IssueDetails(issue, nil)
|
||||||
|
|
||||||
fmt.Println(issue.HTMLURL)
|
fmt.Println(issue.HTMLURL)
|
||||||
|
|
||||||
|
@ -12,7 +12,9 @@ import (
|
|||||||
// ResolveLabelNames returns a list of label IDs for a given list of label names
|
// ResolveLabelNames returns a list of label IDs for a given list of label names
|
||||||
func ResolveLabelNames(client *gitea.Client, owner, repo string, labelNames []string) ([]int64, error) {
|
func ResolveLabelNames(client *gitea.Client, owner, repo string, labelNames []string) ([]int64, error) {
|
||||||
labelIDs := make([]int64, len(labelNames))
|
labelIDs := make([]int64, len(labelNames))
|
||||||
labels, _, err := client.ListRepoLabels(owner, repo, gitea.ListLabelsOptions{})
|
labels, _, err := client.ListRepoLabels(owner, repo, gitea.ListLabelsOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -56,14 +56,14 @@ func CreateLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bo
|
|||||||
Created: time.Now().Unix(),
|
Created: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
client := login.Client()
|
|
||||||
|
|
||||||
if len(token) == 0 {
|
if len(token) == 0 {
|
||||||
if login.Token, err = generateToken(client, user, passwd); err != nil {
|
if login.Token, err = generateToken(login, user, passwd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := login.Client()
|
||||||
|
|
||||||
// Verify if authentication works and get user info
|
// Verify if authentication works and get user info
|
||||||
u, _, err := client.GetMyUserInfo()
|
u, _, err := client.GetMyUserInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,16 +98,19 @@ func CreateLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generateToken creates a new token when given BasicAuth credentials
|
// generateToken creates a new token when given BasicAuth credentials
|
||||||
func generateToken(client *gitea.Client, user, pass string) (string, error) {
|
func generateToken(login config.Login, user, pass string) (string, error) {
|
||||||
gitea.SetBasicAuth(user, pass)(client)
|
client := login.Client(gitea.SetBasicAuth(user, pass))
|
||||||
|
|
||||||
host, _ := os.Hostname()
|
tl, _, err := client.ListAccessTokens(gitea.ListAccessTokensOptions{
|
||||||
tl, _, err := client.ListAccessTokens(gitea.ListAccessTokensOptions{})
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
host, _ := os.Hostname()
|
||||||
tokenName := host + "-tea"
|
tokenName := host + "-tea"
|
||||||
|
|
||||||
|
// append timestamp, if a token with this hostname already exists
|
||||||
for i := range tl {
|
for i := range tl {
|
||||||
if tl[i].Name == tokenName {
|
if tl[i].Name == tokenName {
|
||||||
tokenName += time.Now().Format("2006-01-02_15-04-05")
|
tokenName += time.Now().Format("2006-01-02_15-04-05")
|
||||||
|
@ -20,7 +20,9 @@ import (
|
|||||||
// a matching private key in ~/.ssh/. If no match is found, path is empty.
|
// a matching private key in ~/.ssh/. If no match is found, path is empty.
|
||||||
func findSSHKey(client *gitea.Client) (string, error) {
|
func findSSHKey(client *gitea.Client) (string, error) {
|
||||||
// get keys registered on gitea instance
|
// get keys registered on gitea instance
|
||||||
keys, _, err := client.ListMyPublicKeys(gitea.ListPublicKeysOptions{})
|
keys, _, err := client.ListMyPublicKeys(gitea.ListPublicKeysOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
if err != nil || len(keys) == 0 {
|
if err != nil || len(keys) == 0 {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func PullCheckout(
|
|||||||
client := login.Client()
|
client := login.Client()
|
||||||
pr, _, err := client.GetPullRequest(repoOwner, repoName, index)
|
pr, _, err := client.GetPullRequest(repoOwner, repoName, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("couldn't fetch PR: %s", err)
|
||||||
}
|
}
|
||||||
if err := workaround.FixPullHeadSha(client, pr); err != nil {
|
if err := workaround.FixPullHeadSha(client, pr); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -10,22 +10,17 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
"code.gitea.io/tea/modules/config"
|
"code.gitea.io/tea/modules/config"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
local_git "code.gitea.io/tea/modules/git"
|
local_git "code.gitea.io/tea/modules/git"
|
||||||
"code.gitea.io/tea/modules/print"
|
"code.gitea.io/tea/modules/print"
|
||||||
"code.gitea.io/tea/modules/utils"
|
"code.gitea.io/tea/modules/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreatePull creates a PR in the given repo and prints the result
|
// CreatePull creates a PR in the given repo and prints the result
|
||||||
func CreatePull(login *config.Login, repoOwner, repoName, base, head string, opts *gitea.CreateIssueOption) error {
|
func CreatePull(ctx *context.TeaContext, base, head string, opts *gitea.CreateIssueOption) (err error) {
|
||||||
// open local git repo
|
|
||||||
localRepo, err := local_git.RepoForWorkdir()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Could not open local repo: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// default is default branch
|
// default is default branch
|
||||||
if len(base) == 0 {
|
if len(base) == 0 {
|
||||||
base, err = GetDefaultPRBase(login, repoOwner, repoName)
|
base, err = GetDefaultPRBase(ctx.Login, ctx.Owner, ctx.Repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -33,12 +28,15 @@ func CreatePull(login *config.Login, repoOwner, repoName, base, head string, opt
|
|||||||
|
|
||||||
// default is current one
|
// default is current one
|
||||||
if len(head) == 0 {
|
if len(head) == 0 {
|
||||||
headOwner, headBranch, err := GetDefaultPRHead(localRepo)
|
if ctx.LocalRepo == nil {
|
||||||
|
return fmt.Errorf("no local git repo detected, please specify head branch")
|
||||||
|
}
|
||||||
|
headOwner, headBranch, err := GetDefaultPRHead(ctx.LocalRepo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
head = GetHeadSpec(headOwner, headBranch, repoOwner)
|
head = GetHeadSpec(headOwner, headBranch, ctx.Owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// head & base may not be the same
|
// head & base may not be the same
|
||||||
@ -52,10 +50,10 @@ func CreatePull(login *config.Login, repoOwner, repoName, base, head string, opt
|
|||||||
}
|
}
|
||||||
// title is required
|
// title is required
|
||||||
if len(opts.Title) == 0 {
|
if len(opts.Title) == 0 {
|
||||||
return fmt.Errorf("Title is required")
|
return fmt.Errorf("title is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, _, err := login.Client().CreatePullRequest(repoOwner, repoName, gitea.CreatePullRequestOption{
|
pr, _, err := ctx.Login.Client().CreatePullRequest(ctx.Owner, ctx.Repo, gitea.CreatePullRequestOption{
|
||||||
Head: head,
|
Head: head,
|
||||||
Base: base,
|
Base: base,
|
||||||
Title: opts.Title,
|
Title: opts.Title,
|
||||||
@ -67,7 +65,7 @@ func CreatePull(login *config.Login, repoOwner, repoName, base, head string, opt
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Could not create PR from %s to %s:%s: %s", head, repoOwner, base, err)
|
return fmt.Errorf("could not create PR from %s to %s:%s: %s", head, ctx.Owner, base, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
print.PullDetails(pr, nil, nil)
|
print.PullDetails(pr, nil, nil)
|
||||||
@ -110,7 +108,7 @@ func GetDefaultPRHead(localRepo *local_git.TeaRepo) (owner, branch string, err e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
owner, _ = utils.GetOwnerAndRepo(strings.TrimLeft(url.Path, "/"), "")
|
owner, _ = utils.GetOwnerAndRepo(url.Path, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ func CreatePullReview(ctx *context.TeaContext, idx int64, status gitea.ReviewSta
|
|||||||
// SavePullDiff fetches the diff of a pull request and stores it as a temporary file.
|
// SavePullDiff fetches the diff of a pull request and stores it as a temporary file.
|
||||||
// The path to the file is returned.
|
// The path to the file is returned.
|
||||||
func SavePullDiff(ctx *context.TeaContext, idx int64) (string, error) {
|
func SavePullDiff(ctx *context.TeaContext, idx int64) (string, error) {
|
||||||
diff, _, err := ctx.Login.Client().GetPullRequestDiff(ctx.Owner, ctx.Repo, idx)
|
diff, _, err := ctx.Login.Client().GetPullRequestDiff(ctx.Owner, ctx.Repo, idx, gitea.PullRequestDiffOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -107,10 +107,13 @@ func ParseDiffComments(diffFile string) ([]gitea.CreatePullReviewComment, error)
|
|||||||
|
|
||||||
// OpenFileInEditor opens filename in a text editor, and blocks until the editor terminates.
|
// OpenFileInEditor opens filename in a text editor, and blocks until the editor terminates.
|
||||||
func OpenFileInEditor(filename string) error {
|
func OpenFileInEditor(filename string) error {
|
||||||
editor := os.Getenv("EDITOR")
|
editor := os.Getenv("VISUAL")
|
||||||
if editor == "" {
|
if editor == "" {
|
||||||
fmt.Println("No $EDITOR env is set, defaulting to vim")
|
editor = os.Getenv("EDITOR")
|
||||||
editor = "vim"
|
if editor == "" {
|
||||||
|
fmt.Println("No $VISUAL or $EDITOR env is set, defaulting to vim")
|
||||||
|
editor = "vi"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the full executable path for the editor.
|
// Get the full executable path for the editor.
|
||||||
|
93
modules/task/repo_clone.go
Normal file
93
modules/task/repo_clone.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
local_git "code.gitea.io/tea/modules/git"
|
||||||
|
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
git_config "github.com/go-git/go-git/v5/config"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RepoClone creates a local git clone in the given path, and sets up upstream remote
|
||||||
|
// for fork repos, for good usability with tea.
|
||||||
|
func RepoClone(
|
||||||
|
path string,
|
||||||
|
login *config.Login,
|
||||||
|
repoOwner, repoName string,
|
||||||
|
callback func(string) (string, error),
|
||||||
|
depth int,
|
||||||
|
) (*local_git.TeaRepo, error) {
|
||||||
|
|
||||||
|
repoMeta, _, err := login.Client().GetRepo(repoOwner, repoName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
originURL, err := cloneURL(repoMeta, login)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
auth, err := local_git.GetAuthForURL(originURL, login.Token, login.SSHKey, callback)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// default path behaviour as native git
|
||||||
|
if path == "" {
|
||||||
|
path = repoName
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := git.PlainClone(path, false, &git.CloneOptions{
|
||||||
|
URL: originURL.String(),
|
||||||
|
Auth: auth,
|
||||||
|
Depth: depth,
|
||||||
|
InsecureSkipTLS: login.Insecure,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up upstream remote for forks
|
||||||
|
if repoMeta.Fork && repoMeta.Parent != nil {
|
||||||
|
upstreamURL, err := cloneURL(repoMeta.Parent, login)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
upstreamBranch := repoMeta.Parent.DefaultBranch
|
||||||
|
repo.CreateRemote(&git_config.RemoteConfig{
|
||||||
|
Name: "upstream",
|
||||||
|
URLs: []string{upstreamURL.String()},
|
||||||
|
})
|
||||||
|
repoConf, err := repo.Config()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if b, ok := repoConf.Branches[upstreamBranch]; ok {
|
||||||
|
b.Remote = "upstream"
|
||||||
|
b.Merge = plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", upstreamBranch))
|
||||||
|
}
|
||||||
|
if err = repo.SetConfig(repoConf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &local_git.TeaRepo{Repository: repo}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneURL(repo *gitea.Repository, login *config.Login) (*url.URL, error) {
|
||||||
|
urlStr := repo.CloneURL
|
||||||
|
if login.SSHKey != "" {
|
||||||
|
urlStr = repo.SSHURL
|
||||||
|
}
|
||||||
|
return local_git.ParseURL(urlStr)
|
||||||
|
}
|
@ -33,7 +33,7 @@ func GetOwnerAndRepo(repoPath, user string) (string, string) {
|
|||||||
if len(repoPath) == 0 {
|
if len(repoPath) == 0 {
|
||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
p := strings.Split(repoPath, "/")
|
p := strings.Split(strings.TrimLeft(repoPath, "/"), "/")
|
||||||
if len(p) >= 2 {
|
if len(p) >= 2 {
|
||||||
return p[0], p[1]
|
return p[0], p[1]
|
||||||
}
|
}
|
||||||
|
30
vendor/code.gitea.io/gitea-vet/.changelog.yml
generated
vendored
30
vendor/code.gitea.io/gitea-vet/.changelog.yml
generated
vendored
@ -1,30 +0,0 @@
|
|||||||
# The full repository name
|
|
||||||
repo: gitea/gitea-vet
|
|
||||||
|
|
||||||
# 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:
|
|
||||||
- breaking
|
|
||||||
-
|
|
||||||
name: FEATURES
|
|
||||||
labels:
|
|
||||||
- feature
|
|
||||||
-
|
|
||||||
name: BUGFIXES
|
|
||||||
labels:
|
|
||||||
- bug
|
|
||||||
-
|
|
||||||
name: ENHANCEMENTS
|
|
||||||
labels:
|
|
||||||
- enhancement
|
|
||||||
|
|
||||||
# regex indicating which labels to skip for the changelog
|
|
||||||
skip-labels: skip-changelog|backport\/.+
|
|
45
vendor/code.gitea.io/gitea-vet/.drone.yml
generated
vendored
45
vendor/code.gitea.io/gitea-vet/.drone.yml
generated
vendored
@ -1,45 +0,0 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: compliance
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: check
|
|
||||||
pull: always
|
|
||||||
image: golang:1.14
|
|
||||||
environment:
|
|
||||||
GOPROXY: https://goproxy.cn
|
|
||||||
commands:
|
|
||||||
- make build
|
|
||||||
- make lint
|
|
||||||
- make vet
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: build-master
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build
|
|
||||||
pull: always
|
|
||||||
image: techknowlogick/xgo:latest
|
|
||||||
environment:
|
|
||||||
GOPROXY: https://goproxy.cn
|
|
||||||
commands:
|
|
||||||
- make build
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user