52 Commits

Author SHA1 Message Date
fff1af1029 use secrets for s3 info (#530) 2023-02-16 00:11:01 +01:00
f28f199e85 Changelog for v0.9.1 (#535)
Reviewed-on: https://gitea.com/gitea/tea/pulls/535
Reviewed-by: John Olheiser <john+gitea@jolheiser.com>
2023-02-16 06:22:31 +08:00
c00418e74c Print pull dont crash if it has TeamReviewRequests (#517)
partial backport of #515

Reviewed-on: https://gitea.com/gitea/tea/pulls/517
Reviewed-by: Norwin <noerw@noreply.gitea.io>
2022-09-29 20:35:57 +08:00
6c9b2f8745 move s3 endpoint to secrets 2022-09-13 22:36:45 +02:00
1a256291dc Changelog 0.9.0 (#503)
Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/503
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: John Olheiser <john+gitea@jolheiser.com>
2022-09-14 03:52:46 +08:00
832136b6d4 Add user list command (#427)
Co-authored-by: Matti R <matti@mdranta.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/427
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-committed-by: techknowlogick <techknowlogick@gitea.io>
2022-09-14 03:49:34 +08:00
99e49991bb Add --fields to notification & milestone listings (#422)
Together with #415 this finally adds the field flag to all entity listings.
closes #342

### ⚠️ breaking changes ⚠️
This changes the column names of `tea milestones ls`:

```diff
 - TITLE  | OPEN/CLOSED ISSUES | DUEDATE
 + TITLE  | ITEMS | DUEDATE
```

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/422
Reviewed-by: delvh <dev.lh@web.de>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-09-14 03:08:18 +08:00
bbb287e29e markdown: dont emit ansi sequences when not emitting to tty (#491)
Allows generating a plain text version of an issue (i.e. without colors and other terminal formatting) when storing stdout in a file.
```
tea issue --comments 1 > issue1.txt
```

`IsInteractive()` had to be moved to avoid a recursive import chain.

---

In the future, it would be nice to also respect the `--output` flag. This flag is currently designed for tabular output, but we could add more supported values like `markdown` `ansi`, `plain` to it

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/491
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: delvh <dev.lh@web.de>
2022-09-14 02:35:15 +08:00
5e7c702e07 Clarify command descriptions when no arguments are taken (#496)
This changes the command help string from eg
```
NAME:
   tea label create - Create a label

USAGE:
   tea label [command options] [arguments...]
```
to
```
NAME:
   tea label create - Create a label

USAGE:
   tea label [command options]
```

Hopefully improving usability.

---

edit: this also changes `tea release create` to take the `--tag` flag value optionally via the first argument, as this seems to be a clear UX improvement.

fixes #483

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/496
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: delvh <delvh@noreply.gitea.io>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-09-14 02:14:02 +08:00
b8dbf899d2 Update dependencies (#501)
- The go-sdk update fixes #463

- To review the other updates:
  - glamour [changelog](https://github.com/charmbracelet/glamour/releases), [diff](https://github.com/charmbracelet/glamour/compare/v0.3.0...v0.5.0)
    - enhancement: we now can use `WithPreservedNewLines()` to render markdow the same way as the web ui
  - termenv [changelog](https://github.com/muesli/termenv/releases), [diff](https://github.com/muesli/termenv/compare/v0.9.0...v0.12.0)
    - enhancement: correct feature detection for more terminals
  - xdg [changelog](https://github.com/adrg/xdg/releases), [diff](https://github.com/adrg/xdg/compare/v0.3.3...v0.4.0)
    - no notable changes for us, but good to stay up to date 🤷
  - survey [changelog](https://github.com/AlecAivazis/survey/releases), [diff](https://github.com/AlecAivazis/survey/compare/v2.3.1...v2.3.6)
    - bugfixes
  - cli [changelog](https://github.com/urfave/cli/releases), [diff](https://github.com/urfave/cli/compare/v2.3.0...v2.16.3)
    - bugfixes?

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/501
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-09-14 01:52:44 +08:00
0b8be54186 Rename master branch to main (#495)
This updates drone CI to use the new main branch name `main`.

### ⚠️ breaking

The download URLs on https://dl.gitea.io/tea/master will no longer be updated.

@Owners: is there a way to add a redirect for these URLs from `/tea/master` to `/tea/main`?

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/495
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-08-26 23:34:40 +08:00
2b1bca9e5d Add license for gitea.com/noerw/unidiff-comments (#493)
The updated version just clarifies the license to be MIT, as [added by the original author](865648740d).
fixes #492

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/493
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: John Olheiser <john+gitea@jolheiser.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-08-22 21:14:09 +08:00
d5a258213d Fix CI: disable package-comments lint rule (#494)
There was an unintended change to the rule in revive that now makes our CI trip:
https://github.com/mgechev/revive/pull/694
The package-comments now expected are not really worth writing as these are tiny internal packages, so this change just disables that rule for now.

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/494
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-08-22 20:53:19 +08:00
f83f579dea Show more version info (#486)
Reviewed-on: https://gitea.com/gitea/tea/pulls/486
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Wim <42wim@noreply.gitea.io>
Co-authored-by: 6543 <6543@obermui.de>
Co-committed-by: 6543 <6543@obermui.de>
2022-06-18 22:34:18 +08:00
65535bd948 Use latest go-sdk and bump golang to 1.18 (#485)
Reviewed-on: https://gitea.com/gitea/tea/pulls/485
Reviewed-by: KN4CK3R <kn4ck3r@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: 6543 <6543@obermui.de>
Co-committed-by: 6543 <6543@obermui.de>
2022-06-10 22:31:24 +08:00
02f5f15269 Fix go install for go 1.17 (#481)
Reviewed-on: https://gitea.com/gitea/tea/pulls/481
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Sandro <supersandro2000@noreply.gitea.io>
Co-committed-by: Sandro <supersandro2000@noreply.gitea.io>
2022-04-26 14:07:15 +08:00
883a27b14e Fetch all items where needed. (#475)
Disable pagination in all places where we need all items.
Ideally we'd do multiple paginated requests until the needed items are local, but this is blocked by https://gitea.com/gitea/go-sdk/pulls/473. So this is a stopgap to get correct behaviour.

Fixes #464

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/475
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-04-08 03:44:16 +08:00
e54b32493d fix pipeline to release builds (#479)
fix regression of #478

Reviewed-on: https://gitea.com/gitea/tea/pulls/479
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
2022-03-29 08:29:47 +08:00
329200b1ef Fix running in repos without remote (#472)
For tea, the case of no remotes in the local repo context is equal to `errNotAGiteaRepo`.
This error type is already gracefully handled, so with this change, tea doesn't reject working from a repo without remotes.

fixes #455, closes #465

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/472
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-03-29 08:01:37 +08:00
6663d9f19b Add preference flag_defaults.remote, refactor (#466)
This is a refactor of the code last touched in #458, making the control flow less backwards.

Additionally, this adds a preference `preferences.flag_defaults.remote` that allows to skip this heuristic and set a custom fixed default value for the `--remote` flag.
I'm not sure this is actually needed, I can revert that commit.

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: Andrew Thornton <art27@cantab.net>
Reviewed-on: https://gitea.com/gitea/tea/pulls/466
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-03-29 07:34:14 +08:00
d06f35482e Use golang v1.18 and drop vendor folder (#478)
* remove vendor folder
* use golang v1.18 in ci
* use "go install"
* use vendor folder as cache

Reviewed-on: https://gitea.com/gitea/tea/pulls/478
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
2022-03-29 07:11:11 +08:00
9ab36c55fa Return RFC3339 UTC timestamps for machine-readable output (#470)
### ⚠️ breaking changes ⚠️

- unset timestamps will not be printed as `"0001-01-01 00:00"`, but as empty value `""`
- output formats `csv`, `tsv`, `yaml` output timestamps in UTC instead of local time, and adhere to [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339)

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/470
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: John Olheiser <john.olheiser@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-03-29 06:37:13 +08:00
40e606561f GOPROXY: https://goproxy.io 2022-03-28 23:59:48 +02:00
0970b94552 Fix context requirements of subcommands (#474)
`tea repo fork` and `tea pr checkout` were missing the requirement of a remote repo.

fixes https://gitea.com/gitea/tea/issues/444

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/474
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-03-26 15:32:53 +08:00
dda94a5dea Refactor errorhandling in getReleaseByTag() (#477)
small refactor for consistency

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/477
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-03-25 21:41:08 +08:00
16ba594a28 Interactive issue/pr posting: properly fetch assignees (#476)
Gitea 1.15.0 added a proper API for listing assignee candidates.
imho that release is old enough that tea can start using this without workarounds.

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/476
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-03-19 19:08:58 +08:00
d8f4273ed0 Add TSV to machine-readable formats (#467)
...so no ansi formatting (colors) is emitted in that format (eg `tea labels -o tsv`)

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/467
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-03-13 12:00:21 +08:00
637e3f0666 Fix CI: run make fmt (#469)
Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/469
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2022-03-13 08:53:18 +08:00
ced24ccabb update to new s3 location (#468)
Reviewed-on: https://gitea.com/gitea/tea/pulls/468
2022-03-11 05:01:29 +08:00
fb3e1f75e9 Prefer origin if there are multiple remotes (#458)
Usually, `origin` is the name of a default remote, which corresponds with upstream. This change improves remote selection when non of `main`, `master` nor `trunk` local branches is present.

Co-authored-by: Petr Vaněk <arkamar@atlas.cz>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/458
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: arkamar <arkamar@noreply.gitea.io>
Co-committed-by: arkamar <arkamar@noreply.gitea.io>
2022-03-09 09:35:51 +08:00
0e24009fe9 Fix create milestone with deadline bug (#462)
Fix #461

Reviewed-on: https://gitea.com/gitea/tea/pulls/462
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-03-09 08:47:58 +08:00
cd24fd8e28 Fix few typos in contribution guidelines (#459)
I found few typos in `CONTRIBUTING.md` file.

Co-authored-by: Petr Vaněk <arkamar@atlas.cz>
Reviewed-on: https://gitea.com/gitea/tea/pulls/459
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: arkamar <arkamar@noreply.gitea.io>
Co-committed-by: arkamar <arkamar@noreply.gitea.io>
2022-02-22 22:34:58 +08:00
dd300c1269 Fix typo in bug.md (#460)
occured -> occurred

Reviewed-on: https://gitea.com/gitea/tea/pulls/460
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Ikko Ashimine <eltociear@noreply.gitea.io>
Co-committed-by: Ikko Ashimine <eltociear@noreply.gitea.io>
2022-02-20 00:30:58 +08:00
44c9e7e664 Correct spelling of "wether" to "whether" in usage output (#453)
Noticed "whether" misspelled as "wether" in the output of `tea issues --help` and corrected it in a few locations.

Co-authored-by: Alex Kelly <kellya@arachnitech.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/453
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: kellya <kellya@noreply.gitea.io>
Co-committed-by: kellya <kellya@noreply.gitea.io>
2022-02-01 07:11:37 +08:00
268aa06179 Add bug report issue template (#448)
As we repeatedly ask for information about users' environments, I added this template

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/448
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-12-04 11:20:36 +08:00
a7d83ee416 Simplify build & update installation instructions (#437)
- "revert" the work done in #349. It turns out that this elaborate workaround to get statically built PIEs was only needed due to a bug in go, which got fixed in go 1.16.
- Add an exception to the `-buildmode=pie` flag for OpenBSD, as discovered in #436
- Simplify & update README installation instructions (the Arch AUR package got deleted as it wasn't maintained, so we link to `gitea-tea-git` now.)

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/437
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-12-03 04:01:10 +08:00
a89f51f9ec Implement more issue filters (#400)
This adds new filters to `tea issues ls` and `tea pr ls`, made available in SDK 0.15:

```
--state value                 Filter by state (all|open|closed) (default: open)
--keyword value, -k value     Filter by search string
--labels value, -L value      Comma-separated list of labels to match issues against.
--milestones value, -m value  Comma-separated list of milestones to match issues against.
--author value, -A value
--assignee value, -a value
--mentions value, -M value
--from value, -F value        Filter by activity after this date
--until value, -u value       Filter by activity before this date
```

Note: I felt free to change parameter names as exposed by SDK & API, as the names exposed by them are partially bollocks (eg `mentioned_by`) and or inconsistent with usage in other commands (eg `tea times --until`)

fixes #376, related #323

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/400
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-12-03 03:26:48 +08:00
d2295828d0 Fix resolving of URLs in markdown (#401)
Path-only URLs need an absolute reference to be resolved against for printing in markdown
Previously we resolved against the URL to the resource we were operating on (eg comment or issue URL).
The markdown renderer in the web UI resolves all such URLs relative to the repo base URL. This PR adopts this behaviour in tea, by trimming the URL to a repo base URL via regex.

This makes a custom patch to our markdown renderer `glamour` obsolete, which turned out to be an incorrect patch, meaning we can make use of upstream glamour again.

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/401
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-12-03 02:59:02 +08:00
dc16643e0d Improve Documentation (#433)
- document more assumptions about usage context of gitea
- improve some flag descriptions (#432, #377)

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/433
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-12-03 02:33:56 +08:00
ac25e89ebf Add tea repo create-from-template (#408)
I went with a new subcommand instead of `tea repo create --template`, as the options are quite different (bool instead of values, partially different set)

fixes #362

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/408
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-10-20 09:31:35 +08:00
819cc1ab21 Add tea clone (#411)
Adds a new subcommand to clone repos:
```
tea clone --login try --depth 1 norwin/test
tea clone gitea/tea
tea clone noerw/tea           # will set up `master` to track `upstream` remote
tea clone try.gitea.io/noerw/test # will automatically set --login
```

This is just a replacement for `git clone` with small benefits:
- [x] does not depend on `git`, as tea ships with go-git
- [x] spares you typing of URLs and autoselects https/ssh based on your login config
- [x] forked repos: set up origin + upstream remote

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-on: https://gitea.com/gitea/tea/pulls/411
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-10-18 20:09:27 +08:00
78a95f1ca4 Allow editing multiline prompts with external text editor (#429)
- Adds a new `Preferences` struct to the config, initially only containing `Editor: bool (default false)`.
  This struct will be serialized to configs once there is a first tea induced change to the config (eg `tea login default <name>` or `tea login add`).
- Use external editor for all multiline prompts if preferred.

We already had a function for starting a texteditor for diff reviews; it does not really make sense to replace it with `survey.Editor`, as there is a big interface mismatch: survey expects strings as inputs, while our diff functions operate on files,

fixes #424

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/429
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-10-14 22:36:08 +08:00
5b77345b03 Add tea repo fork (#410)
Adds a new subcommand to fork repos.

To specify the repo to fork, use the `--repo` flag. This feels a bit weird, other tea commands would put this as the first argument.
I decided to follow the flag style, as this is what all other subcommands of `tea repo` do. We might want to reconsider and make the primary subject of such commands an argument, instead of an required flag.. see #430

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-on: https://gitea.com/gitea/tea/pulls/410
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-10-14 15:27:39 +08:00
23ce7b351d fix CI release upload: switch to woodpecker/plugin-s3 (#428)
- this fixes the CI release upload issues, as the docker image for this is freshly built (unlike the mostly unmaintained "official" drone plugins), thus containing current CA certs needed for letsencrypt since 2021-09-31.
- woodpecker is a drone-ci fork maintained partially by @6543. it's API compatible with current drone plugins afaik

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/428
Reviewed-by: Alexey 〒erentyev <axifive@noreply.gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-10-10 20:48:51 +08:00
375ece06d2 fix lint regression (#425)
On PR #421 CI did not work and it was force merged. Thus we managed to have failing CI on master.. :( sorry

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/425
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-10-05 01:43:06 +08:00
4ffd994549 Add tea whoami command (#426)
The User print will be used in future for list of users for admin

Co-authored-by: Matti R <matti@mdranta.net>
Reviewed-on: https://gitea.com/gitea/tea/pulls/426
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Reviewed-by: Alexey 〒erentyev <axifive@noreply.gitea.io>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-committed-by: techknowlogick <techknowlogick@gitea.io>
2021-10-05 01:41:59 +08:00
58aaa17e7e Show issue reactions (#421)
```
$ tea issue 230

   #230 issue/pull details: show reactions (open)

  @6543 created 2020-10-22 16:39

  since reactions are utf8 now and most terminals too, we can display them nicely :)

  https://gitea.com/api/v1/repos/gitea/tea/issues/230/reactions

  --------

  1x 🎉  |  1x 👀  |  1x :gitea:  |  1x 👍  |  1x 👎  |  1x 😆  |  1x 😕  |  1x ❤️
```

caveats:
- reactions are not returned as UTF8 (as was claimed in #230), so they need to be parsed. the library I use doesn't (and can't → :gitea:) support all reactions available in gitea
- currently only for issues, as reactions for comments mean an additional API request for each comment..

fixes #230

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/421
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-10-01 16:13:32 +08:00
7a05be436c Add more flags to tea repo create (#409)
adds the `--template` and `--trustmodel` flags

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/409
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-29 04:42:51 +08:00
3cf084cb96 PR listing: add --fields & expose additional fields (#415)
This PR adds the `--fields` flag to `tea pr ls` (#342), and exposes more fields specific to the `PullRequest` type:
```
   --fields value, -f value   Comma-separated list of fields to print.
                              Available values:
                              index,state,author,author-id,url,title,body,mergeable,base,base-commit,head,diff,patch,created,updated,deadline,assignees,milestone,labels,comments
                              (default: "index,title,state,author,milestone,updated,labels")

```

Co-authored-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/415
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: justusbunsi <justusbunsi@noreply.gitea.io>
Co-committed-by: justusbunsi <justusbunsi@noreply.gitea.io>
2021-09-29 04:36:33 +08:00
1e59dee685 Add tea org create <name> (#420)
fixes #287, fixes #363

Co-authored-by: Norwin <git@nroo.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/420
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-29 04:32:16 +08:00
42e423470c Fix notification example (#416)
Co-authored-by: justusbunsi <61625851+justusbunsi@users.noreply.github.com>
Reviewed-on: https://gitea.com/gitea/tea/pulls/416
Reviewed-by: Norwin <noerw@noreply.gitea.io>
Reviewed-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: justusbunsi <justusbunsi@noreply.gitea.io>
Co-committed-by: justusbunsi <justusbunsi@noreply.gitea.io>
2021-09-29 03:29:48 +08:00
555f1ae516 Makefile: add STATIC=true for static PIE builds (#349)
- `make build` + `make install` now support the `STATIC=true` parameter, creating statically linked builds that are also position independent executables
- this requires CGO and a static libc on the build system
- `CGO_ENABLED=0` is set for all make build targets, unless `STATIC=true` is set
- Debug symbols are stripped (`-s -w`) for all make build targets
- Release binaries are built statically by gox (no PIE), as before.

I also took the liberty to declutter the makefile from unused & duplicated variables.

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/349
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
2021-09-24 00:01:07 +08:00
1779 changed files with 2052 additions and 435105 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
Dockerfile
tea

View File

@ -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: gitea-artifacts 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: gitea-artifacts 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: gitea-artifacts 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

View 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
View File

@ -6,3 +6,4 @@ tea
dist/ dist/
.vscode/ .vscode/
vendor/

View File

@ -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]

View File

@ -1,5 +1,44 @@
# 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 ## [v0.8.0](https://gitea.com/gitea/tea/releases/tag/v0.8.0) - 2021-09-22
* BREAKING * BREAKING

View File

@ -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

View File

@ -2,16 +2,16 @@ ARG GOVERSION="1.16.2"
FROM golang:${GOVERSION}-alpine AS buildenv FROM golang:${GOVERSION}-alpine AS buildenv
ARG CGO_ENABLED="0"
ARG GOOS="linux" ARG GOOS="linux"
COPY . $GOPATH/src/ COPY . $GOPATH/src/
WORKDIR $GOPATH/src WORKDIR $GOPATH/src
RUN apk add --quiet --no-cache \ RUN apk add --quiet --no-cache \
build-base \
make \ make \
git && \ git && \
make build make clean build STATIC=true
FROM scratch FROM scratch
ARG VERSION="0.7.0" ARG VERSION="0.7.0"

View File

@ -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,25 +21,28 @@ 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)) 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
@ -77,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
@ -119,34 +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 .PHONY: build-image
build-image: build-image:
docker build --build-arg VERSION=$(TEA_VERSION) -t gitea/tea:$(TEA_VERSION_TAG) . 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:
@ -154,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/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

View File

@ -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:
```sh - To compile the source yourself with the recommended flags & tags:
git clone https://gitea.com/gitea/tea.git ```sh
cd tea git clone https://gitea.com/gitea/tea.git # or: tea clone gitea.com/gitea/tea ;)
make cd tea
``` 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

View File

@ -1,7 +1,8 @@
// 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.
//+build vendor //go:build vendor
// +build vendor
package main package main

55
cmd/admin.go Normal file
View 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
View 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
}

View File

@ -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
View File

@ -0,0 +1,89 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package 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
}

View File

@ -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
} }
} }

View File

@ -21,15 +21,19 @@ type CsvFlag struct {
// NewCsvFlag creates a CsvFlag, while setting its usage string and default values // NewCsvFlag creates a CsvFlag, while setting its usage string and default values
func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag { func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag {
var availableDesc string
if len(availableValues) != 0 {
availableDesc = " Available values:"
}
return &CsvFlag{ return &CsvFlag{
AvailableFields: availableValues, AvailableFields: availableValues,
StringFlag: cli.StringFlag{ StringFlag: cli.StringFlag{
Name: name, Name: name,
Aliases: aliases, Aliases: aliases,
Value: strings.Join(defaults, ","), Value: strings.Join(defaults, ","),
Usage: fmt.Sprintf(`Comma-separated list of %s. Available values: Usage: fmt.Sprintf(`Comma-separated list of %s.%s
%s %s
`, usage, strings.Join(availableValues, ",")), `, usage, availableDesc, strings.Join(availableValues, ",")),
}, },
} }
} }

View File

@ -5,14 +5,6 @@
package flags package flags
import ( 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" "github.com/urfave/cli/v2"
) )
@ -44,13 +36,6 @@ var OutputFlag = cli.StringFlag{
Usage: "Output format. (csv, simple, table, tsv, yaml)", 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 // PaginationPageFlag provides flag for pagination options
var PaginationPageFlag = cli.StringFlag{ var PaginationPageFlag = cli.StringFlag{
Name: "page", Name: "page",
@ -93,13 +78,6 @@ var AllDefaultFlags = append([]cli.Flag{
&RemoteFlag, &RemoteFlag,
}, LoginOutputFlags...) }, LoginOutputFlags...)
// IssuePRFlags defines flags that should be available on issue & pr listing flags.
var IssuePRFlags = append([]cli.Flag{
&StateFlag,
&PaginationPageFlag,
&PaginationLimitFlag,
}, AllDefaultFlags...)
// NotificationFlags defines flags that should be available on notifications. // NotificationFlags defines flags that should be available on notifications.
var NotificationFlags = append([]cli.Flag{ var NotificationFlags = append([]cli.Flag{
NotificationStateFlag, NotificationStateFlag,
@ -121,82 +99,6 @@ var NotificationStateFlag = NewCsvFlag(
[]string{"unread", "pinned"}, []string{"unread", "pinned"},
) )
// IssuePREditFlags defines flags for properties of issues and PRs
var IssuePREditFlags = append([]cli.Flag{
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"d"},
},
&cli.StringFlag{
Name: "assignees",
Aliases: []string{"a"},
Usage: "Comma-separated list of usernames to assign",
},
&cli.StringFlag{
Name: "labels",
Aliases: []string{"L"},
Usage: "Comma-separated list of labels to assign",
},
&cli.StringFlag{
Name: "deadline",
Aliases: []string{"D"},
Usage: "Deadline timestamp to assign",
},
&cli.StringFlag{
Name: "milestone",
Aliases: []string{"m"},
Usage: "Milestone to assign",
},
}, LoginRepoFlags...)
// GetIssuePREditFlags parses all IssuePREditFlags
func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
opts := gitea.CreateIssueOption{
Title: ctx.String("title"),
Body: ctx.String("description"),
Assignees: strings.Split(ctx.String("assignees"), ","),
}
var err error
date := ctx.String("deadline")
if date != "" {
t, err := dateparse.ParseAny(date)
if err != nil {
return nil, err
}
opts.Deadline = &t
}
client := ctx.Login.Client()
labelNames := strings.Split(ctx.String("labels"), ",")
if len(labelNames) != 0 {
if client == nil {
client = ctx.Login.Client()
}
if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil {
return nil, err
}
}
if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 {
if client == nil {
client = ctx.Login.Client()
}
ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName)
if err != nil {
return nil, fmt.Errorf("Milestone '%s' not found", milestoneName)
}
opts.Milestone = ms.ID
}
return &opts, nil
}
// FieldsFlag generates a flag selecting printable fields. // FieldsFlag generates a flag selecting printable fields.
// To retrieve the value, use f.GetValues() // To retrieve the value, use f.GetValues()
func FieldsFlag(availableFields, defaultFields []string) *CsvFlag { func FieldsFlag(availableFields, defaultFields []string) *CsvFlag {

161
cmd/flags/issue_pr.go Normal file
View 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
}

View File

@ -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)

View File

@ -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
} }

View File

@ -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,
} }

View File

@ -5,11 +5,15 @@
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"
) )
@ -23,8 +27,9 @@ var CmdIssuesList = cli.Command{
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{issueFieldsFlag}, flags.IssuePRFlags...), Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssueListingFlags...),
} }
// RunIssuesList list issues // RunIssuesList list issues
@ -36,16 +41,57 @@ 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 {

View File

@ -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,

View File

@ -23,6 +23,7 @@ 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: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.StringFlag{ &cli.StringFlag{

View File

@ -17,6 +17,7 @@ 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: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.IntFlag{ &cli.IntFlag{

View File

@ -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{

View File

@ -17,6 +17,7 @@ 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: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.IntFlag{ &cli.IntFlag{

View File

@ -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",

View File

@ -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},
} }

View File

@ -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},
} }

View File

@ -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

View File

@ -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
} }

View File

@ -15,7 +15,11 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var notifTypeFlag = flags.NewCsvFlag("types", "subject types to filter by", []string{"t"}, 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) []string{"issue", "pull", "repository", "commit"}, nil)
// CmdNotificationsList represents a sub command of notifications to list notifications // CmdNotificationsList represents a sub command of notifications to list notifications
@ -24,8 +28,12 @@ var CmdNotificationsList = cli.Command{
Aliases: []string{"list"}, Aliases: []string{"list"},
Usage: "List notifications", Usage: "List notifications",
Description: `List notifications`, Description: `List notifications`,
ArgsUsage: " ", // command does not accept arguments
Action: RunNotificationsList, Action: RunNotificationsList,
Flags: append([]cli.Flag{notifTypeFlag}, flags.NotificationFlags...), Flags: append([]cli.Flag{
notifyFieldsFlag,
notifyTypeFlag,
}, flags.NotificationFlags...),
} }
// RunNotificationsList list notifications // RunNotificationsList list notifications
@ -40,7 +48,7 @@ func RunNotificationsList(ctx *cli.Context) error {
} }
var types []gitea.NotifySubjectType var types []gitea.NotifySubjectType
typesStr, err := notifTypeFlag.GetValues(ctx) typesStr, err := notifyTypeFlag.GetValues(ctx)
if err != nil { if err != nil {
return err return err
} }
@ -66,7 +74,17 @@ func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects [
listOpts.Page = 1 listOpts.Page = 1
} }
fields, err := notifyFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
if all { if all {
// add repository to the default fields
if !cmd.IsSet("fields") {
fields = append(fields, "repository")
}
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{ news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
ListOptions: listOpts, ListOptions: listOpts,
Status: status, Status: status,
@ -84,6 +102,6 @@ func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects [
log.Fatal(err) log.Fatal(err)
} }
print.NotificationsList(news, ctx.Output, all) print.NotificationsList(news, ctx.Output, fields)
return nil return nil
} }

View File

@ -28,7 +28,7 @@ var CmdNotificationsMarkRead = cli.Command{
if err != nil { if err != nil {
return err return err
} }
if !flags.NotificationStateFlag.IsSet() { if !cmd.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusUnread)} filter = []string{string(gitea.NotifyStatusUnread)}
} }
return markNotificationAs(cmd, filter, gitea.NotifyStatusRead) return markNotificationAs(cmd, filter, gitea.NotifyStatusRead)
@ -49,7 +49,7 @@ var CmdNotificationsMarkUnread = cli.Command{
if err != nil { if err != nil {
return err return err
} }
if !flags.NotificationStateFlag.IsSet() { if !cmd.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusRead)} filter = []string{string(gitea.NotifyStatusRead)}
} }
return markNotificationAs(cmd, filter, gitea.NotifyStatusUnread) return markNotificationAs(cmd, filter, gitea.NotifyStatusUnread)
@ -70,7 +70,7 @@ var CmdNotificationsMarkPinned = cli.Command{
if err != nil { if err != nil {
return err return err
} }
if !flags.NotificationStateFlag.IsSet() { if !cmd.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusUnread)} filter = []string{string(gitea.NotifyStatusUnread)}
} }
return markNotificationAs(cmd, filter, gitea.NotifyStatusPinned) return markNotificationAs(cmd, filter, gitea.NotifyStatusPinned)
@ -107,10 +107,10 @@ func markNotificationAs(cmd *context.TeaContext, filterStates []string, targetSt
opts := gitea.MarkNotificationOptions{Status: states, ToStatus: targetState} opts := gitea.MarkNotificationOptions{Status: states, ToStatus: targetState}
if allRepos { if allRepos {
_, err = client.ReadNotifications(opts) _, _, err = client.ReadNotifications(opts)
} else { } else {
cmd.Ensure(context.CtxRequirement{RemoteRepo: true}) cmd.Ensure(context.CtxRequirement{RemoteRepo: true})
_, err = client.ReadRepoNotifications(cmd.Owner, cmd.Repo, opts) _, _, err = client.ReadRepoNotifications(cmd.Owner, cmd.Repo, opts)
} }
// TODO: print all affected notification subject URLs // TODO: print all affected notification subject URLs
@ -121,7 +121,7 @@ func markNotificationAs(cmd *context.TeaContext, filterStates []string, targetSt
if err != nil { if err != nil {
return err return err
} }
_, err = client.ReadNotification(id, targetState) _, _, err = client.ReadNotification(id, targetState)
if err != nil { if err != nil {
return err return err
} }

View File

@ -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,18 +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, 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
} }

View File

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

View File

@ -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,

View File

@ -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{
@ -71,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)
} }

View File

@ -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")
} }

View File

@ -18,17 +18,17 @@ 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...),
} }

View File

@ -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
} }

View File

@ -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,

View File

@ -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"),

View File

@ -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

View File

@ -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")

View File

@ -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
} }

View File

@ -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,
} }

View File

@ -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...),
} }
@ -86,9 +95,24 @@ func runRepoCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
client := ctx.Login.Client() client := ctx.Login.Client()
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)

View 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
View 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
}

28
cmd/whoami.go Normal file
View 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
},
}

82
go.mod
View File

@ -1,40 +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.15.0 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.3.1 github.com/AlecAivazis/survey/v2 v2.3.6
github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c // indirect github.com/adrg/xdg v0.4.0
github.com/adrg/xdg v0.3.3
github.com/alecthomas/chroma v0.9.2 // indirect
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/charmbracelet/glamour v0.3.0 github.com/charmbracelet/glamour v0.5.0
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/enescakir/emoji v1.0.0
github.com/go-git/go-git/v5 v5.4.2 github.com/go-git/go-git/v5 v5.4.2
github.com/hashicorp/go-version v1.3.0 // indirect github.com/muesli/termenv v0.12.0
github.com/kevinburke/ssh_config v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/microcosm-cc/bluemonday v1.0.15 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.9.0
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/sergi/go-diff v1.2.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.1 // indirect golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
github.com/yuin/goldmark v1.4.0 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.5 // indirect
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )
replace github.com/charmbracelet/glamour => github.com/noerw/glamour v0.3.0-patch 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
)

193
go.sum
View File

@ -1,62 +1,64 @@
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.15.0 h1:tsNhxDM/2N1Ohv1Xq5UWrht/esg0WmtRj4wsHVHriTg= code.gitea.io/sdk/gitea v0.15.1-0.20220831004139-a0127ed0e7fe h1:PeLyxnUZE85QuJtBZ4P8qCQcgWG5Ked67mlNgr0WkCQ=
code.gitea.io/sdk/gitea v0.15.0/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= 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.3.1 h1:lzkuHA60pER7L4eYL8qQJor4bUWlJe4V0gqAT19tdOA= github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
github.com/AlecAivazis/survey/v2 v2.3.1/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg= 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/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c h1:FP7mMdsXy0ybzar1sJeIcZtaJka0U/ZmLTW4wRpolYk= github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 h1:NsReiLpErIPzRrnogAXYwSoU7txA977LjDGrbkewJbg=
github.com/ProtonMail/go-crypto v0.0.0-20210707164159-52430bf6b52c/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/adrg/xdg v0.3.3 h1:s/tV7MdqQnzB1nKY8aqHvAMD+uCiuEDzVB5HLRY849U= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.3.3/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/chroma v0.8.2/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
github.com/alecthomas/chroma v0.9.2 h1:yU1sE2+TZbLIQPMk30SolL2Hn53SR/Pv750f7qZ/XMs=
github.com/alecthomas/chroma v0.9.2/go.mod h1:eMuEnpA18XbG/WhOWtCzJHS7WqEtDAI+HxdwoW0nVSk=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
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-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/charmbracelet/glamour v0.5.0 h1:wu15ykPdB7X6chxugG/NNfDUbyyrCLV9XBalj5wdu3g=
github.com/charmbracelet/glamour v0.5.0/go.mod h1:9ZRtG19AUIzcTm7FGLGbq3D5WKQ5UyZBbQsMQN0XIqc=
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/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/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.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
@ -70,28 +72,27 @@ 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/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/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.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= 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-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 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 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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=
@ -100,13 +101,13 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= 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/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
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/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.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
@ -115,20 +116,17 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
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.6/go.mod h1:HOT/6NaBlR0f9XlxD3zolN6Z3N8Lp4pvhp+jLS5ihnI= github.com/microcosm-cc/bluemonday v1.0.17/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
github.com/microcosm-cc/bluemonday v1.0.15 h1:J4uN+qPng9rvkBZBoBb8YGR+ijuklIMpSOZZLjYpbeY= github.com/microcosm-cc/bluemonday v1.0.20 h1:flpzsq4KU3QIYAYGV/szUat7H+GPOXR0B2JU5A1Wp8Y=
github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3ZpqOF2+sdFr0Om+ce30= 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/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0=
github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8=
github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw=
github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc=
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.3.0-patch h1:yc3wdbUIySok6KYeX5BtWnlj+PvP1uYeCeTSwq2rtSw=
github.com/noerw/glamour v0.3.0-patch/go.mod h1:TzF0koPZhqq0YVBNL100cPHznAAjVj7fksX2RInwjGw=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 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=
@ -137,69 +135,68 @@ 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/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/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/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.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.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM=
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= 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.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.14 h1:jwww1XQfhJN7Zm+/a1ZA/3WUiEBEroYFNTiV3dKwM8U=
github.com/yuin/goldmark v1.4.0 h1:OtISOGfH6sOWa1/qXqqAiOIAO6Z5J3AEAE18WAq6BiQ= github.com/yuin/goldmark v1.4.14/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/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.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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-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-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -208,25 +205,28 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
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-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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-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-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/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-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -235,13 +235,11 @@ 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.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -250,10 +248,11 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
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
View File

@ -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)
}
return version
} }
var appDescription = `tea is a productivity helper for Gitea. It can be used to manage most entities on one var appDescription = `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.
` `
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.

View File

@ -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() {

View File

@ -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
@ -144,7 +163,7 @@ func AddLogin(login *Login) error {
// Client returns a client to operate Gitea API. You may provide additional modifiers // Client returns a client to operate Gitea API. You may provide additional modifiers
// for the client like gitea.SetBasicAuth() for customization // for the client like gitea.SetBasicAuth() for customization
func (l *Login) Client(options ...func(*gitea.Client)) *gitea.Client { 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)

View File

@ -99,6 +99,10 @@ func InitCommand(ctx *cli.Context) *TeaContext {
} }
} }
if len(remoteFlag) == 0 {
remoteFlag = config.GetPreferences().FlagDefaults.Remote
}
// try to read local git repo & extract context: if repoFlag specifies a valid path, read repo in that dir, // 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 // 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 {
@ -153,28 +157,38 @@ 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.
if len(gitConfig.Remotes) > 1 && len(remoteValue) == 0 {
// if master branch is present, use it as the default remote
mainBranches := []string{"main", "master", "trunk"}
for _, b := range mainBranches {
masterBranch, ok := gitConfig.Branches[b]
if ok {
if len(masterBranch.Remote) > 0 {
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 { for remote := range gitConfig.Remotes {
remoteValue = remote remoteValue = remote
} break
if len(gitConfig.Remotes) > 1 {
// if master branch is present, use it as the default remote
mainBranches := []string{"main", "master", "trunk"}
for _, b := range mainBranches {
masterBranch, ok := gitConfig.Branches[b]
if ok {
if len(masterBranch.Remote) > 0 {
remoteValue = masterBranch.Remote
}
break
}
}
} }
} }

View File

@ -22,13 +22,18 @@ type URLParser struct {
func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) { func (p *URLParser) Parse(rawURL string) (u *url.URL, err error) {
rawURL = strings.TrimSpace(rawURL) rawURL = strings.TrimSpace(rawURL)
// convert the weird git ssh url format to a canonical url: if !protocolRe.MatchString(rawURL) {
// git@gitea.com:gitea/tea -> ssh://git@gitea.com/gitea/tea // convert the weird git ssh url format to a canonical url:
if !protocolRe.MatchString(rawURL) && // git@gitea.com:gitea/tea -> ssh://git@gitea.com/gitea/tea
strings.Contains(rawURL, ":") && 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
View 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)
}

View File

@ -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()))

View File

@ -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

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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"
@ -55,7 +56,11 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error {
if (state == gitea.ReviewStateComment && len(codeComments) == 0) || 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
} }
@ -65,6 +70,7 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error {
// DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $VISUAL or $EDITOR to comment on diff, // 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 {

View File

@ -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,
) )

View File

@ -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
}

View File

@ -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,14 +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":
if x.Deadline == nil { if x.Deadline == nil {
return "" return ""
} }
return FormatTime(*x.Deadline) 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

View File

@ -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 {

View File

@ -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 ""
}

View File

@ -12,26 +12,51 @@ import (
) )
// NotificationsList prints a listing of notification threads // NotificationsList prints a listing of notification threads
func NotificationsList(news []*gitea.NotificationThread, output string, showRepository bool) { func NotificationsList(news []*gitea.NotificationThread, output string, fields []string) {
headers := []string{ var printables = make([]printable, len(news))
"ID", for i, x := range news {
"Status", printables[i] = &printableNotification{x}
"Type",
"State",
"Index",
"Title",
}
if showRepository {
headers = append(headers, "Repository")
} }
t := tableFromItems(fields, printables, isMachineReadable(output))
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",
}
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"
} }
// if pull or Issue get Index 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
@ -39,31 +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
status := "read" case "type":
if n.Pinned { return string(n.Subject.Type)
status = "pinned"
} else if n.Unread {
status = "unread"
}
item := []string{ case "state":
fmt.Sprint(n.ID), return string(n.Subject.State)
status,
string(n.Subject.Type), case "title":
string(n.Subject.State), return n.Subject.Title
index,
n.Subject.Title, case "repo", "repository":
} return n.Repository.FullName
if showRepository {
item = append(item, n.Repository.FullName)
}
t.addRowSlice(item)
}
if t.Len() != 0 {
t.print(output)
} }
return ""
} }

View File

@ -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 {

View File

@ -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,8 +99,11 @@ func formatReviews(reviews []*gitea.PullReview) string {
case gitea.ReviewStateApproved, case gitea.ReviewStateApproved,
gitea.ReviewStateRequestChanges, gitea.ReviewStateRequestChanges,
gitea.ReviewStateRequestReview: gitea.ReviewStateRequestReview:
if r, ok := reviewByUser[review.Reviewer.ID]; !ok || review.Submitted.After(r.Submitted) { // only user reviews are supported no team review requests
reviewByUser[review.Reviewer.ID] = review if review.Reviewer != nil {
if r, ok := reviewByUser[review.Reviewer.ID]; !ok || review.Submitted.After(r.Submitted) {
reviewByUser[review.Reviewer.ID] = review
}
} }
} }
} }
@ -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 { // PullFields are all available fields to print with PullsList()
if pr == nil { var PullFields = []string{
continue "index",
"state",
"author",
"author-id",
"url",
"title",
"body",
"mergeable",
"base",
"base-commit",
"head",
"diff",
"patch",
"created",
"updated",
"deadline",
"assignees",
"milestone",
"labels",
"comments",
}
func printPulls(pulls []*gitea.PullRequest, output string, fields []string) {
labelMap := map[int64]string{}
var printables = make([]printable, len(pulls))
machineReadable := isMachineReadable(output)
for i, x := range pulls {
// pre-serialize labels for performance
for _, label := range x.Labels {
if _, ok := labelMap[label.ID]; !ok {
labelMap[label.ID] = formatLabel(label, !machineReadable, "")
}
} }
author := pr.Poster.FullName // store items with printable interface
if len(author) == 0 { printables[i] = &printablePull{x, &labelMap}
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),
)
} }
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 ""
}

View File

@ -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,
) )

View File

@ -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":

View File

@ -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

View File

@ -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
View 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 ""
}

View File

@ -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)

View File

@ -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
} }

View File

@ -101,7 +101,9 @@ func CreateLogin(name, token, user, passwd, sshKey, giteaURL string, insecure bo
func generateToken(login config.Login, user, pass string) (string, error) { func generateToken(login config.Login, user, pass string) (string, error) {
client := login.Client(gitea.SetBasicAuth(user, pass)) client := login.Client(gitea.SetBasicAuth(user, pass))
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
} }

View File

@ -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
} }

View File

@ -108,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
} }

View File

@ -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
} }

View 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)
}

View File

@ -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]
} }

View File

@ -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\/.+

View File

@ -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

View File

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

View File

@ -1,23 +0,0 @@
linters:
enable:
- deadcode
- dogsled
- dupl
- errcheck
- gocognit
- goconst
- gocritic
- gocyclo
- gofmt
- golint
- gosimple
- govet
- maligned
- misspell
- prealloc
- staticcheck
- structcheck
- typecheck
- unparam
- unused
- varcheck

View File

@ -1,11 +0,0 @@
## [v0.2.1](https://gitea.com/gitea/gitea-vet/releases/tag/v0.2.1) - 2020-08-15
* BUGFIXES
* Split migration check to Deps and Imports (#9)
## [0.2.0](https://gitea.com/gitea/gitea-vet/pulls?q=&type=all&state=closed&milestone=1272) - 2020-07-20
* FEATURES
* Add migrations check (#5)
* BUGFIXES
* Correct Import Paths (#6)

View File

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

View File

@ -1,22 +0,0 @@
GO ?= go
.PHONY: build
build:
$(GO) build
.PHONY: fmt
fmt:
$(GO) fmt ./...
.PHONY: vet
vet: build
$(GO) vet ./...
$(GO) vet -vettool=gitea-vet ./...
.PHONY: lint
lint:
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
export BINARY="golangci-lint"; \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell $(GO) env GOPATH)/bin v1.24.0; \
fi
golangci-lint run --timeout 5m

View File

@ -1,11 +0,0 @@
# gitea-vet
[![Build Status](https://drone.gitea.com/api/badges/gitea/gitea-vet/status.svg)](https://drone.gitea.com/gitea/gitea-vet)
`go vet` tool for Gitea
| Analyzer | Description |
|------------|-----------------------------------------------------------------------------|
| Imports | Checks for import sorting. stdlib->code.gitea.io->other |
| License | Checks file headers for some form of `Copyright...YYYY...Gitea/Gogs` |
| Migrations | Checks for black-listed packages in `code.gitea.io/gitea/models/migrations` |

View File

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

View File

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

View File

@ -1,77 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package checks
import (
"errors"
"os/exec"
"strings"
"golang.org/x/tools/go/analysis"
)
var Migrations = &analysis.Analyzer{
Name: "migrations",
Doc: "check migrations for black-listed packages.",
Run: checkMigrations,
}
var (
migrationDepBlockList = []string{
"code.gitea.io/gitea/models",
}
migrationImpBlockList = []string{
"code.gitea.io/gitea/modules/structs",
}
)
func checkMigrations(pass *analysis.Pass) (interface{}, error) {
if !strings.EqualFold(pass.Pkg.Path(), "code.gitea.io/gitea/models/migrations") {
return nil, nil
}
if _, err := exec.LookPath("go"); err != nil {
return nil, errors.New("go was not found in the PATH")
}
depsCmd := exec.Command("go", "list", "-f", `{{join .Deps "\n"}}`, "code.gitea.io/gitea/models/migrations")
depsOut, err := depsCmd.Output()
if err != nil {
return nil, err
}
deps := strings.Split(string(depsOut), "\n")
for _, dep := range deps {
if stringInSlice(dep, migrationDepBlockList) {
pass.Reportf(0, "code.gitea.io/gitea/models/migrations cannot depend on the following packages: %s", migrationDepBlockList)
return nil, nil
}
}
impsCmd := exec.Command("go", "list", "-f", `{{join .Imports "\n"}}`, "code.gitea.io/gitea/models/migrations")
impsOut, err := impsCmd.Output()
if err != nil {
return nil, err
}
imps := strings.Split(string(impsOut), "\n")
for _, imp := range imps {
if stringInSlice(imp, migrationImpBlockList) {
pass.Reportf(0, "code.gitea.io/gitea/models/migrations cannot import the following packages: %s", migrationImpBlockList)
return nil, nil
}
}
return nil, nil
}
func stringInSlice(needle string, haystack []string) bool {
for _, h := range haystack {
if strings.EqualFold(needle, h) {
return true
}
}
return false
}

View File

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

View File

@ -1,20 +0,0 @@
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

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

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