Compare commits

..

No commits in common. "main" and "v0.1.1" have entirely different histories.
main ... v0.1.1

996 changed files with 344624 additions and 13957 deletions

View File

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

View File

@ -1,20 +0,0 @@
{
"name": "Tea DevContainer",
"image": "mcr.microsoft.com/devcontainers/go:1.22-bullseye",
"features": {
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {}
},
"customizations": {
"vscode": {
"settings": {},
"extensions": [
"editorconfig.editorconfig",
"golang.go",
"stylelint.vscode-stylelint",
"DavidAnson.vscode-markdownlint",
"ms-azuretools.vscode-docker",
"GitHub.vscode-pull-request-github"
]
}
}
}

View File

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

194
.drone.yml Normal file
View File

@ -0,0 +1,194 @@
---
kind: pipeline
name: default
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/tea
steps:
- name: build
pull: always
image: golang:1.13
environment:
GOPROXY: https://goproxy.cn
commands:
- make clean
- make vet
- make lint
- make fmt-check
- make misspell-check
- make test-vendor
- make build
when:
event:
- push
- tag
- pull_request
- name: unit-test
pull: always
image: golang:1.13
commands:
- make unit-test-coverage
settings:
group: test
when:
branch:
- master
event:
- push
- pull_request
- name: release-test
pull: always
image: golang:1.13
commands:
- make test
settings:
group: test
when:
branch:
- "release/*"
event:
- push
- pull_request
- name: tag-test
pull: always
image: golang:1.13
commands:
- make test
settings:
group: test
when:
event:
- tag
- name: static
pull: always
image: techknowlogick/xgo:latest
commands:
- export PATH=$PATH:$GOPATH/bin
- make release
when:
event:
- push
- tag
- name: gpg-sign
pull: always
image: plugins/gpgsign:1
settings:
detach_sign: true
excludes:
- "dist/release/*.sha256"
files:
- "dist/release/*"
environment:
GPGSIGN_KEY:
from_secret: gpgsign_key
GPGSIGN_PASSPHRASE:
from_secret: gpgsign_passphrase
when:
event:
- push
- tag
- name: tag-release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: "/tea/${DRONE_TAG##v}"
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
when:
event:
- tag
- name: release-branch-release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: "/tea/${DRONE_BRANCH##release/v}"
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
when:
branch:
- "release/*"
event:
- push
- name: release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: /tea/master
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
when:
branch:
- master
event:
- push
- name: gitea
pull: always
image: plugins/gitea-releases:1
settings:
files:
- "dist/release/*"
base_url: https://gitea.com
api_key:
from_secret: gitea_token
when:
event:
- tag
- name: discord
pull: always
image: appleboy/drone-discord:1.0.0
environment:
DISCORD_WEBHOOK_ID:
from_secret: discord_webhook_id
DISCORD_WEBHOOK_TOKEN:
from_secret: discord_webhook_token
when:
event:
- push
- tag
- pull_request
status:
- changed
- failure

View File

@ -1,30 +0,0 @@
---
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 main build](https://dl.gitea.com/tea/main/)
- 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)

View File

@ -1,75 +0,0 @@
name: goreleaser
on:
push:
branches: [ main ]
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: git fetch --force --tags
- uses: actions/setup-go@v5
with:
go-version-file: "go.mod"
- name: import gpg
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
- name: goreleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser-pro
version: "~> v1"
args: release --nightly
env:
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
S3_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
GPGSIGN_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }}
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-image:
runs-on: ubuntu-latest
env:
DOCKER_ORG: gitea
DOCKER_LATEST: nightly
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # all history for all branches and tags
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v6
env:
ACTIONS_RUNTIME_TOKEN: '' # See https://gitea.com/gitea/act_runner/issues/119
with:
context: .
file: ./Dockerfile
platforms: |
linux/amd64
linux/arm64
push: true
tags: |
gitea/tea:latest

View File

@ -1,42 +0,0 @@
name: goreleaser
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: git fetch --force --tags
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: import gpg
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
fingerprint: CC64B1DB67ABBEECAB24B6455FC346329753F4B0
- name: goreleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser-pro
version: "~> v1"
args: release
env:
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
S3_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
GORELEASER_FORCE_TOKEN: 'gitea'
GPGSIGN_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }}
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}

View File

@ -1,30 +0,0 @@
name: check-and-test
on:
- pull_request
jobs:
check-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
- name: lint and build
run: |
make clean
make vet
make lint
make fmt-check
make misspell-check
make docs-check
make build
env:
GOPROXY: https://goproxy.io,direct
- name: test and coverage
run: |
make test
make unit-test-coverage
env:
GOPROXY: https://goproxy.io,direct

10
.gitignore vendored
View File

@ -1,14 +1,4 @@
tea
/gitea-vet
/gitea-vet.exe
.idea/
.history/
dist/
.vscode/
vendor/
coverage.out
dist/

View File

@ -1,12 +0,0 @@
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "usage: $0 <path>"
exit 1
fi
SUM=$(shasum -a 256 "$1" | cut -d' ' -f1)
BASENAME=$(basename "$1")
echo -n "${SUM} ${BASENAME}" > "$1".sha256

View File

@ -1,124 +0,0 @@
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
goos:
- darwin
- linux
- windows
- freebsd
goarch:
- amd64
- arm
- arm64
goarm:
- "5"
- "6"
- "7"
ignore:
- goos: darwin
goarch: arm
- goos: darwin
goarch: ppc64le
- goos: darwin
goarch: s390x
- goos: windows
goarch: ppc64le
- goos: windows
goarch: s390x
- goos: windows
goarch: arm
goarm: "5"
- goos: windows
goarch: arm
goarm: "6"
- goos: windows
goarch: arm
goarm: "7"
- goos: windows
goarch: arm64
- goos: freebsd
goarch: ppc64le
- goos: freebsd
goarch: s390x
- goos: freebsd
goarch: arm
goarm: "5"
- goos: freebsd
goarch: arm
goarm: "6"
- goos: freebsd
goarch: arm
goarm: "7"
- goos: freebsd
goarch: arm64
flags:
- -trimpath
ldflags:
- -s -w -X main.Version={{ .Version }}
binary: >-
{{ .ProjectName }}-
{{- .Version }}-
{{- .Os }}-
{{- if eq .Arch "amd64" }}amd64
{{- else if eq .Arch "amd64_v1" }}amd64
{{- else if eq .Arch "386" }}386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}-{{ .Arm }}{{ end }}
no_unique_dist_dir: true
hooks:
post:
- cmd: xz -k -9 {{ .Path }}
dir: ./dist/
- cmd: sh .goreleaser.checksum.sh {{ .Path }}
- cmd: sh .goreleaser.checksum.sh {{ .Path }}.xz
blobs:
-
provider: s3
bucket: "{{ .Env.S3_BUCKET }}"
region: "{{ .Env.S3_REGION }}"
folder: "tea/{{.Version}}"
extra_files:
- glob: ./**.xz
- glob: ./**.sha256
archives:
- format: binary
name_template: "{{ .Binary }}"
allow_different_binary_count: true
checksum:
name_template: 'checksums.txt'
extra_files:
- glob: ./**.xz
force_token: gitea
signs:
-
signature: "${artifact}.sig"
artifacts: checksum
stdin: '{{ .Env.GPGSIGN_PASSPHRASE }}'
args: ["--batch", "-u", "{{ .Env.GPG_FINGERPRINT }}", "--output", "${signature}", "--detach-sign", "${artifact}"]
snapshot:
name_template: "{{ .Branch }}-devel"
nightly:
name_template: "{{ .Branch }}"
gitea_urls:
api: https://gitea.com/api/v1
download: https://gitea.com
release:
extra_files:
- glob: ./**.xz
- glob: ./**.xz.sha256
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

View File

@ -16,6 +16,7 @@ warningCode = 1
[rule.increment-decrement]
[rule.var-naming]
[rule.var-declaration]
[rule.package-comments]
[rule.range]
[rule.receiver-naming]
[rule.time-naming]

View File

@ -1,241 +0,0 @@
# Changelog
## [v0.9.1](https://gitea.com/gitea/tea/releases/tag/v0.9.1) - 2023-02-15
* BUGFIXES
* Print pull dont crash if it has TeamReviewRequests (#517)
## [v0.9.0](https://gitea.com/gitea/tea/releases/tag/v0.9.0) - 2022-09-13
* BREAKING
* Rename master branch to main (#495)
* Return RFC3339 UTC timestamps for machine-readable output (#470)
* FEATURES
* Allow editing multiline prompts with external text editor (#429)
* Add `tea admin user list` (#427)
* Add `tea whoami` command (#426)
* Add `tea org create <name>` (#420)
* Add `tea clone` (#411)
* Add `tea repo fork` (#410)
* Add `tea repo create-from-template` (#408)
* BUGFIXES
* Fetch all items where needed. (#475)
* Fix running in repos without remote (#472)
* Add TSV to machine-readable formats (#467)
* Fix create milestone with deadline bug (#462)
* Fix resolving of URLs in markdown (#401)
* ENHANCEMENTS
* Don't emit ANSI sequences when not emitting to TTY for markdown (#491)
* Show more version info (#486)
* Add preference `flag_defaults.remote`, refactor (#466)
* Add `--fields` to notification & milestone listings (#422)
* PR listing: add --fields & expose additional fields (#415)
* Add more flags to `tea repo create` (#409)
* Implement more issue filters (#400)
* MISC
* Simplify build & update installation instructions (#437)
* Clarify command descriptions when no arguments are taken (#496)
* Improve Documentation (#433)
* Use golang v1.18 and drop vendor folder (#478)
* Correct spelling of "wether" to "whether" in usage output (#453)
## [v0.8.0](https://gitea.com/gitea/tea/releases/tag/v0.8.0) - 2021-09-22
* BREAKING
* `tea notifications --all` has moved to `tea notifications --mine` (#389)
* `tea notifications` now only works with the context of a remote repo. (#389)
To run this outside of a local git dir, run either tea n `--mine` or `tea n --repo <my/repo>`
* FEATURES
* Add `tea pr merge` (#348)
* BUGFIXES
* Don't skip reading the local repo when `--repo` specifies a repo slug (#398)
* Fix adding login without token on private instances (#392)
* Correctly match login by ssh host with port (#391)
* Fix printing issue deadline (#388)
* Return useful error on wrong sshkey path (#374)
* Fix parsing of `--description` for issue/pr create (#371)
* Add missing flags (#369)
* Check negative limit command parameter (#358) (#359)
* Add missing flags to org & labels subcommands (#357)
* ENHANCEMENTS
* Don't require a body for comment PR reviews (#399)
* Accept more main branch names for login detection (#396)
* Make local repo optional for `tea pr create`(#393)
* Notifications Add State Field (#384)
* Improve error messages (#370)
* Add tab completion for fish shell (#364)
* Text editor selection: follow unix defacto standards (#356)
* MISC
* Update Dependencies (#390)
## [v0.7.1](https://gitea.com/gitea/tea/releases/tag/v0.7.1) - 2021-08-27
* BUILD
* Enable release builds for darwin/arm64 (#360)
## [v0.7.0](https://gitea.com/gitea/tea/releases/tag/v0.7.0) - 2021-03-12
* BREAKING
* `tea issue create`: move `-b` flag to `-d` (#331)
* Drop `tea notif` shorthand in favor of `tea n` (#307)
* FEATURES
* Add commands for reviews (#315)
* Add `tea comment` and show comments of issues/pulls (#313)
* Add interactive mode for `tea milestone create` (#310)
* Add command to install shell completion (#309)
* Implement PR closing and reopening (#304)
* Add interactive mode for `tea issue create` (#302)
* BUGFIXES
* Introduce workaround for missing pull head sha (#340)
* Don't exit if we can't find a local repo with a remote matching to a login (#336)
* Don't push before creating a pull (#334)
* InitCommand() robustness (#327)
* `tea comment`: handle piped stdin (#322)
* ENHANCEMENTS
* Allow checking out PRs with deleted head branch (#341)
* Markdown renderer: detect terminal width, resolve relative URLs (#332)
* Add more issue / pr creation parameters (#331)
* Improve `tea time` (#319)
* `tea pr checkout`: dont create local branches (#314)
* Add `tea issues --fields`, allow printing labels (#312)
* Add more command shorthands (#307)
* Show PR CI status (#306)
* Make PR workflow helpers more robust (#300)
## [v0.6.0](https://gitea.com/gitea/tea/releases/tag/v0.6.0) - 2020-12-11
* BREAKING
* Add `tea repos search`, improve repo listing (#215)
* Add Detail View for Login (#212)
* FEATURES
* Add interactive mode for `tea pr create` (#279)
* Add organization delete command (#270)
* Add organization list command (#264)
* BUGFIXES
* Forces needed arguments to `tea ms issues` (#297)
* Subcommands work outside of git repos (#285)
* Fix repo flag ignores local repo for login detection (#285)
* Improve ssh handling (#277)
* Issue create return web url (#257)
* Support prerelease gitea instances (#252)
* Fix `tea pr create` within same repo (#248)
* Handle login name case-insensitive on all comands (#227)
* ENHANCEMENTS
* Add `tea login delete` (#296)
* Release delete: add --delete-tag & --confirm (#286)
* Sorted milestones list (#281)
* Pull clean & checkout use token for http(s) auth (#275)
* Show more infos in pull detail view (#271)
* Specify fields to print on `tea repos list` (#223)
* Print times in local timezone (#217)
* Issue create/edit print details (#214)
* Improve `tea logout` (#213)
* Added a shorthand for notifications (#209)
* Common subcommand naming scheme (#208)
* `tea pr checkout`: fetch via ssh if available (#192)
* Major refactor of codebase
* BUILD
* Use gox to cross-compile (#274)
* DOCS
* Update Docs to new code structure (#247)
## [v0.5.0](https://gitea.com/gitea/tea/releases/tag/v0.5.0) - 2020-09-27
* BREAKING
* Add Login Manage Functions (#182)
* FEATURES
* Add Release Subcomands (#195)
* Render Markdown and colorize labels table (#181)
* Add BasicAuth & Interactive for Login (#174)
* Add milestones subcomands (#149)
* BUGFIXES
* Fix Pulls Create (#202)
* Pulls create: detect head branch repo owner (#193)
* Fix Labels Delete (#180)
* ENHANCEMENTS
* Add Pagination Options for List Subcomands (#204)
* Issues/Pulls: Details show State (#196)
* Make issues & pulls subcommands consistent (#188)
* Update SDK to v0.13.0 (#179)
* More Options To Specify Repo (#178)
* Add Repo Create subcomand & enhancements (#173)
* Times: format duration as seconds for machine-readable outputs (#168)
* Add user message to login list view (#166)
## [v0.4.1](https://gitea.com/gitea/tea/releases/tag/v0.4.1) - 2020-09-13
* BUGFIXES
* Notification don't relay on a repo (#159)
## [v0.4.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1264) - 2020-07-18
* FEATURES
* Add notifications subcomand (#148)
* Add subcomand 'pulls create' (#144)
* BUGFIXES
* Fix Login Detection By Repo Param (#151)
* Fix Login List Output (#150)
* Fix --ssh-key Option (#135)
* ENHANCEMENTS
* Subcomand Login Show List By Default (#152)
* BUILD
* Migrate src-d/go-git to go-git/go-git (#128)
* Migrate gitea-sdk to v0.12.0 (#133)
* Migrate yaml lib (#130)
* Add gitea-vet (#121)
## [v0.3.1](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1265) - 2020-06-15
* BUGFIXES
* --ssh-key should be string not bool (#135) (#137)
* modules/git: fix dropped error (#127)
* Issues details: add missing newline (#126)
## [v0.3.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1227) - 2020-04-22
* FEATURES
* Add `tea pulls [checkout | clean]` commands (#93 #97 #107) (#105)
* Add `tea open` (#101)
* Add `tea issues [open|close]` commands (#99)
* ENHANCEMENTS
* Ignore PRs for `tea issues` (#111)
* Add --state flag filter to issue & PR lists (#100)
## [v0.2.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=538) - 2020-03-06
* FEATURES
* Add `tea times` command (#54)
* ENHANCEMENTS
* Upgrade urfave/cli to v2 version (#85)
* Add --remote flag to add/create subcommands (#77)
* BUILD
* Upgrade gitea/go-sdk to 2020-01-03 (#81)
* Update stretchr/testify v1.3.0 -> v1.4.0 (#83)
* Improve makefile to enable goproxy when go get tools (#98)
## [v0.1.2](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-11-15
* BUILD
* Fix typo in drone (#75)
## [v0.1.1](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-11-15
* FEATURES
* Add repos subcommand (#65)
* ENHANCEMENTS
* Minor improvements to command-line language (#66)
## [v0.1.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-10-28
* BREAKING
* Changed git config determination to go-git (#41) [continue #45] (#62)
* FEATURES
* Add labels commands (#36)
* BUGFIXES
* Fix out -o flag (#53)
* Fix log formatting, refactor flag definition in cmd/labels.go (#52)
* ENHANCEMENTS
* List label description (#60)
* Use Different Remote Repos (#58)
* Unified output (#14) (#40)
* Added global appendable Flags (#12) (#39)
* BUILD
* Change .drone.yml to new format (#33)
* DOCS
* Add install guide from brew on README (#61)

View File

@ -7,6 +7,8 @@
- [Bug reports](#bug-reports)
- [Discuss your design](#discuss-your-design)
- [Testing redux](#testing-redux)
- [Vendoring](#vendoring)
- [Translation](#translation)
- [Code review](#code-review)
- [Styleguide](#styleguide)
- [Sign-off your work](#sign-off-your-work)
@ -18,31 +20,36 @@
## Introduction
This document explains how to contribute changes to TEA.
This document explains how to contribute changes to the Gitea project.
It assumes you have followed the
[installation instructions](https://docs.gitea.io/en-us/).
Sensitive security-related issues should be reported to
[security@gitea.io](mailto:security@gitea.io).
For configuring IDE or code editor to develop Gitea see [IDE and code editor configuration](https://github.com/go-gitea/gitea/tree/master/contrib/ide)
For configuring IDE or code editor to develop Gitea see [IDE and code editor configuration](contrib/ide/)
## Bug reports
Please search the issues on the issue tracker with a variety of keywords
to ensure your bug is not already reported.
If unique, [open an issue](https://gitea.com/gitea/tea/issues/new).
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new)
and answer the questions so we can understand and reproduce the
problematic behavior.
Please write clear, concise instructions so we can reproduce the behavior—
To show us that the issue you are having is in Gitea itself, please
write clear, concise instructions so we can reproduce the behavior—
even if it seems obvious. The more detailed and specific you are,
the faster we can fix the issue. Check out [How to Report Bugs
Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
Please be kind, remember that TEA comes at no cost to you, and you're
Please be kind, remember that Gitea comes at no cost to you, and you're
getting free help.
## Discuss your design
The project welcomes submissions. If you want to change or add something,
please let everyone know what you're working on—[file an issue](https://gitea.com/gitea/tea/issues/new)!
please let everyone know what you're working on—[file an issue](https://github.com/go-gitea/gitea/issues/new)!
Significant changes must go through the change proposal process
before they can be accepted. To create a proposal, file an issue with
your proposed changes documented, and make sure to note in the title
@ -56,14 +63,55 @@ high-level discussions.
## Testing redux
Before sending code out for review, run all the test by executing: `make test`
Since TEA is an cli tool it should be obvious to test your feature locally first.
Before sending code out for review, run all the tests for the
whole tree to make sure the changes don't break other usage
and keep the compatibility on upgrade. To make sure you are
running the test suite exactly like we do, you should install
the CLI for [Drone CI](https://github.com/drone/drone), as
we are using the server for continous testing, following [these
instructions](http://docs.drone.io/cli-installation/). After that,
you can simply call `drone exec --local --build-event "pull_request"` within
your working directory and it will try to run the test suite locally.
## Vendoring
We keep a cached copy of dependencies within the `vendor/` directory,
managing updates via [dep](https://github.com/golang/dep).
Pull requests should only include `vendor/` updates if they are part of
the same change, be it a bugfix or a feature addition.
The `vendor/` update needs to be justified as part of the PR description,
and must be verified by the reviewers and/or merger to always reference
an existing upstream commit.
You can find more information on how to get started with it on the [dep project website](https://golang.github.io/dep/docs/introduction.html).
## Translation
We do all translation work inside [Crowdin](https://crowdin.com/project/gitea).
The only translation that is maintained in this git repository is
[`en_US.ini`](https://github.com/go-gitea/gitea/blob/master/options/locale/locale_en-US.ini)
and is synced regularily to Crowdin. Once a translation has reached
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
included in the next released version.
## Building Gitea
Generally, the go build tools are installed as-needed in the `Makefile`.
An exception are the tools to build the CSS and images.
- To build CSS: Install [Node.js](https://nodejs.org/en/download/package-manager)
with `npm` and then run `npm install` and `make generate-stylesheets`.
- To build Images: ImageMagick, inkscape and zopflipng binaries must be
available in your `PATH` to run `make generate-images`.
## Code review
Changes to TEA must be reviewed before they are accepted—no matter who
makes the change, even if they are an owner or a maintainer. We use Gitea's
pull request & review workflow to do that. Gitea ensure every PR is reviewed by at least 2 maintainers.
Changes to Gitea must be reviewed before they are accepted—no matter who
makes the change, even if they are an owner or a maintainer. We use GitHub's
pull request workflow to do that. And, we also use [LGTM](http://lgtm.co)
to 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
the *[How to get faster PR reviews](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews)* guide;
@ -80,41 +128,6 @@ Some of the key points:
## Styleguide
### Commands
- Subcommands should follow the following structure:
```
tea <noun> <verb> [<noun>] [<flags>]
```
for example:
```
tea issues list
tea pulls create
tea teams add user --team x --user y
```
- Commands should accept nouns as singular & plural by making use of the `Aliases` field.
- The default action without a verb is `list`.
- There is a standard set of verbs: `list/ls`, `create`, `edit`, `delete`
- `ls` lists objects with filter options, and applies pagination where available.
- `delete` should show info what is deleted and ask user again, if force flag`-y` is not set
- Verbs that accept large numbers of flags provide an interactive mode when called without any arguments or flags.
- Try to reuse as many flag definitions as possible, see `cmd/flags/flags.go`.
- Always make sure that the help texts are properly set, and as concise as possible.
### Internal Module Structure
- `cmd`: only contains command/flag options for `urfave/cli`
- subcommands are in a subpackage named after its parent command
- `modules/task`: contain func for doing something with gitea
(e.g. create token by user/passwd)
- `modules/print`: contain all functions that print to stdout
- `modules/config`: config tea & login things
- `modules/interact`: contain functions to interact with user by prompts
- `modules/git`: do git related stuff (get info/push/pull/checkout/...)
- `modules/utils`: helper functions used by other functions
### Code Style
Use `make fmt`, check with `make lint`.
For imports you should use the following format (_without_ the comments)
```go
import (
@ -151,7 +164,25 @@ commit automatically with `git commit -s`.
## Release Cycle
Before we reach v1 there is no fixed release cycle.
We adopted a release schedule to streamline the process of working
on, finishing, and issuing releases. The overall goal is to make a
minor release every two months, which breaks down into one month of
general development followed by one month of testing and polishing
known as the release freeze. All the feature pull requests should be
merged in the first month of one release period. And, during the frozen
period, a corresponding release branch is open for fixes backported from
master. Release candidates are made during this period for user testing to
obtain a final version that is maintained in this branch. A release is
maintained by issuing patch releases to only correct critical problems
such as crashes or security issues.
Major release cycles are bimonthly. They always begin on the 25th and end on
the 24th (i.e., the 25th of December to February 24th).
During a development cycle, we may also publish any necessary minor releases
for the previous version. For example, if the latest, published release is
v1.2, then minor changes for the previous release—e.g., v1.1.0 -> v1.1.1—are
still possible.
## Maintainers
@ -160,7 +191,7 @@ maintainers](MAINTAINERS). Every PR **MUST** be reviewed by at least
two maintainers (or owners) before it can get merged. A maintainer
should be a contributor of Gitea (or Gogs) and contributed at least
4 accepted PRs. A contributor should apply as a maintainer in the
[Discord](https://discord.gg/Gitea) #develop channel. The owners
[Discord](https://discord.gg/NsatcWJ) #develop channel. The owners
or the team maintainers may invite the contributor. A maintainer
should spend some time on code reviews. If a maintainer has no
time to do that, they should apply to leave the maintainers team
@ -171,15 +202,12 @@ to the maintainers team. If a maintainer is inactive for more than 3
months and forgets to leave the maintainers team, the owners may move
him or her from the maintainers team to the advisors team.
For security reasons, Maintainers should use 2FA for their accounts and
if possible provide gpg signed commits.
if possible provide gpg signed commits.
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
https://help.github.com/articles/signing-commits-with-gpg/
## Owners
This repo is part of the Gitea project and as such part of that project's
governance.
Since Gitea is a pure community organization without any company support,
to keep the development healthy we will elect three owners every year. All
contributors may vote to elect up to three candidates, one of which will
@ -193,7 +221,7 @@ https://help.github.com/articles/securing-your-account-with-two-factor-authentic
After the election, the new owners should proactively agree
with our [CONTRIBUTING](CONTRIBUTING.md) requirements in the
[Discord](https://discord.gg/Gitea) #general channel. Below are the
[Discord](https://discord.gg/NsatcWJ) #general channel. Below are the
words to speak:
```
@ -202,9 +230,22 @@ I'm honored to having been elected an owner of Gitea, I agree with
and lead the development of Gitea.
```
To honor the past owners, here's the history of the owners and the time
they served:
* 2016-11-04 ~ 2017-12-31
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
* 2018-01-01 ~ 2018-12-31
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
## Versions
tea has the `master` branch as a tip branch and has version branches
Gitea has the `master` branch as a tip branch and has version branches
such as `release/v0.9`. `release/v0.9` is a release branch and we will
tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept
pull requests on the `release/v0.9` branch and publish a `v0.9.1` tag,
@ -220,9 +261,9 @@ be reviewed by two maintainers and must pass the automatic tests.
Code that you contribute should use the standard copyright header:
```
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Copyright 2018 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.
```
Files in the repository contain copyright from the year they are added

View File

@ -1,10 +0,0 @@
FROM cgr.dev/chainguard/go:latest AS build
COPY . /build/
WORKDIR /build
RUN make build
FROM cgr.dev/chainguard/static:latest
COPY --from=build /build/tea /tea
VOLUME [ "/app" ]
ENV HOME="/app"
ENTRYPOINT ["/tea"]

View File

@ -1,63 +0,0 @@
# comparing git forge commandline interfaces
[tea]: https://gitea.com/gitea/tea
[sip]: https://gitea.com/jolheiser/sip
[gitlab]: https://github.com/makkes/gitlab-cli
[glab]: https://github.com/profclems/glab
[gh]: https://cli.github.com
last update: 2020-12-11
## general
/ | [tea][tea] | [sip][sip] | [gitlab][gitlab] | [gh][gh]
-----------------------|:-----:|:-----:|:-----:|:-----:
forge|gitea|gitea|gitlab|github
official forge support|✓|✘|✘|✓
dev status|adding features|maintenance||
platform|any|any|any|any
## philosophy
/ | [tea][tea] | [sip][sip] | [gitlab][gitlab] | [gh][gh]
-----------------------|:-----:|:-----:|:-----:|:-----:
aims to replace git cli|✘|||✓
works with decentralization in mind|✓|✓|✓|✘
per-repo setup needed|✘||✓|✘
workflow helpers|✓|||
interactive mode |[(✓)](https://gitea.com/gitea/tea/issues?type=all&state=open&labels=&milestone=0&assignee=0&q=interactive)|✘| |✓
programmatic mode|✓|||✓
machine readable output|✓|||
follows XDG spec|✓|||
## features
/ | [tea][tea] | [sip][sip] | [gitlab][gitlab] | [gh][gh]
-----------------------|:-----:|:-----:|:-----:|:-----:
open web UI|✓|||
search repos|✓|||
search issues|✘|✓||
textual item search filter syntax|✘|✓||
CRUD repos|[(✓)](https://gitea.com/gitea/tea/issues/239)|||
CRUD issues|[(✓)](https://gitea.com/gitea/tea/issues/229)|||
CRUD milestones|[(✓)](https://gitea.com/gitea/tea/issues/246)|||
CRUD releases|✓|||
CRUD labels|✓|||
CRUD PRs|✓|||
CRUD time tracking|✓|||x
CRUD orgs|[(✓)](https://gitea.com/gitea/tea/issues/287)|||
create PRs from local repo|✓|||
create PRs from remote repo|✓|||
code review|[u](https://gitea.com/gitea/tea/issues/131)|||
merge PRs||||
read comments|[u](https://gitea.com/gitea/tea/issues/172)|||
post comments||||
manage CI|✘|✘|✓|
manage notifications|[(✓)]()|||
administration|[u](https://gitea.com/gitea/tea/issues/161)|✘||✘
markdown rendering|✓|||✓
issue import/export|[u](https://gitea.com/gitea/tea/issues/132)|||
checkout PRs|✓|||
- ✓: supported
- (✓): partial support
- u: upcoming
- ✘: not supported
- ?: unknown

View File

@ -1,4 +1,5 @@
Copyright (c) 2016 The Gitea Authors
Copyright (c) 2015 The Gogs 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

159
Makefile
View File

@ -1,12 +1,31 @@
DIST := dist
IMPORT := code.gitea.io/tea
export GO111MODULE=off
GO ?= go
SED_INPLACE := sed -i
SHASUM ?= shasum -a 256
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
GOFILES := $(shell find . -name "*.go" -type f ! -path "*/bindata.go")
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")
GOFMT ?= gofmt -s
GOFLAGS := -i -v
EXTRA_GOFLAGS ?=
MAKE_VERSION := $(shell make -v | head -n 1)
ifneq ($(DRONE_TAG),)
VERSION ?= $(subst v,,$(DRONE_TAG))
TEA_VERSION ?= $(VERSION)
@ -14,31 +33,27 @@ else
ifneq ($(DRONE_BRANCH),)
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
else
VERSION ?= main
VERSION ?= master
endif
TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
endif
TEA_VERSION_TAG ?= $(shell sed 's/+/_/' <<< $(TEA_VERSION))
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
LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)"
# override to allow passing additional goflags via make CLI
override GOFLAGS := $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)'
PACKAGES ?= $(shell $(GO) list ./...)
PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
SOURCES ?= $(shell find . -name "*.go" -type f)
# OS specific vars.
TAGS ?=
ifeq ($(OS), Windows_NT)
EXECUTABLE := tea.exe
VET_TOOL := gitea-vet.exe
else
EXECUTABLE := tea
VET_TOOL := gitea-vet
endif
# $(call strip-suffix,filename)
strip-suffix = $(firstword $(subst ., ,$(1)))
.PHONY: all
all: build
@ -53,23 +68,28 @@ fmt:
.PHONY: vet
vet:
# Default vet
$(GO) vet $(PACKAGES)
# Custom vet
$(GO) build code.gitea.io/gitea-vet
$(GO) vet -vettool=$(VET_TOOL) $(PACKAGES)
.PHONY: lint
lint: install-lint-tools
$(GO) run github.com/mgechev/revive@v1.3.2 -config .revive.toml ./... || exit 1
lint:
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/mgechev/revive; \
fi
revive -config .revive.toml -exclude=./vendor/... ./... || exit 1
.PHONY: misspell-check
misspell-check: install-lint-tools
$(GO) run github.com/client9/misspell/cmd/misspell@latest -error -i unknwon,destory $(GOFILES)
misspell-check:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -error -i unknwon,destory $(GOFILES)
.PHONY: misspell
misspell: install-lint-tools
$(GO) run github.com/client9/misspell/cmd/misspell@latest -w -i unknwon $(GOFILES)
misspell:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -w -i unknwon $(GOFILES)
.PHONY: fmt-check
fmt-check:
@ -81,53 +101,88 @@ fmt-check:
exit 1; \
fi;
.PHONY: docs
docs:
$(GO) run . docs --out docs/CLI.md
.PHONY: docs-check
docs-check:
@DIFF=$$($(GO) run . docs | diff docs/CLI.md -); \
if [ -n "$$DIFF" ]; then \
echo "Please run 'make docs' and commit the result:"; \
echo "$$DIFF"; \
exit 1; \
fi;
.PHONY: test
test:
$(GO) test -tags='sqlite sqlite_unlock_notify' $(PACKAGES)
GO111MODULE=on $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' $(PACKAGES)
.PHONY: unit-test-coverage
unit-test-coverage:
$(GO) test -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: tidy
tidy:
$(GO) mod tidy
.PHONY: vendor
vendor:
GO111MODULE=on $(GO) mod tidy && GO111MODULE=on $(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
check: test
.PHONY: install
install: $(SOURCES)
@echo "installing to $(shell $(GO) env GOPATH)/bin/$(EXECUTABLE)"
$(GO) install -v $(BUILDMODE) $(GOFLAGS)
install: $(wildcard *.go)
$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
.PHONY: build
build: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES)
$(GO) build $(BUILDMODE) $(GOFLAGS) -o $@
GO111MODULE=on $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
.PHONY: build-image
build-image:
docker build --build-arg VERSION=$(TEA_VERSION) -t gitea/tea:$(TEA_VERSION_TAG) .
.PHONY: release
release: release-dirs release-windows release-linux release-darwin release-copy release-compress release-check
install-lint-tools:
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) install github.com/mgechev/revive@v1.3.2; \
.PHONY: release-dirs
release-dirs:
mkdir -p $(DIST)/binaries $(DIST)/release
.PHONY: release-windows
release-windows:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \
fi
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) install github.com/client9/misspell/cmd/misspell@latest; \
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out tea-$(VERSION) .
ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries
endif
.PHONY: release-linux
release-linux:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \
fi
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out tea-$(VERSION) .
ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries
endif
.PHONY: release-darwin
release-darwin:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \
fi
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out tea-$(VERSION) .
ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries
endif
.PHONY: release-copy
release-copy:
cd $(DIST); for file in `find /build -type f -name "*"`; do cp $${file} ./release/; done;
.PHONY: release-compress
release-compress:
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(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;
.PHONY: release-check
release-check:
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;

160
README.md
View File

@ -1,137 +1,67 @@
# <img alt='tea logo' src='https://gitea.com/repo-avatars/550-80a3a8c2ab0e2c2d69f296b7f8582485' height="40"/> *T E A*
# Gitea Command Line Tool for Go
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Release](https://raster.shields.io/badge/dynamic/json.svg?label=release&url=https://gitea.com/api/v1/repos/gitea/tea/releases&query=$[0].tag_name)](https://gitea.com/gitea/tea/releases)
[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea)
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/tea)](https://goreportcard.com/report/code.gitea.io/tea) [![GoDoc](https://pkg.go.dev/badge/code.gitea.io/tea?status.svg)](https://godoc.org/code.gitea.io/tea)
![Tea Release Status](https://gitea.com/gitea/tea/actions/workflows/release-nightly.yml/badge.svg)
## The official CLI for Gitea
![demo gif](./demo.gif)
```
tea - command line tool to interact with Gitea
version 0.8.0-preview
USAGE
tea command [subcommand] [command options] [arguments...]
DESCRIPTION
tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.
tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.
COMMANDS
help, h Shows a list of commands or help for one command
ENTITIES:
issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests
labels, label Manage issue labels
milestones, milestone, ms List and create milestones
releases, release, r Manage releases
release assets, release asset, r a Manage release attachments
times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations
repos, repo Show repository details
comment, c Add a comment to an issue / pr
HELPERS:
open, o Open something of the repository in web browser
notifications, notification, n Show notifications
clone, C Clone a repository locally
SETUP:
logins, login Log in to a Gitea server
logout Log out from a Gitea server
shellcompletion, autocomplete Install shell completion for tea
whoami Show current logged in user
OPTIONS
--help, -h show help (default: false)
--version, -v print the version (default: false)
EXAMPLES
tea login add # add a login once to get started
tea pulls # list open pulls for the repo in $PWD
tea pulls --repo $HOME/foo # list open pulls for the repo in $HOME/foo
tea pulls --remote upstream # list open pulls for the repo pointed at by
# your local "upstream" git remote
# list open pulls for any gitea repo at the given login instance
tea pulls --repo gitea/tea --login gitea.com
tea milestone issues 0.7.0 # view open issues for milestone '0.7.0'
tea issue 189 # view contents of issue 189
tea open 189 # open web ui for issue 189
tea open milestones # open web ui for milestones
# send gitea desktop notifications every 5 minutes (bash + libnotify)
while :; do tea notifications --mine -o simple | xargs -i notify-send {}; sleep 300; done
ABOUT
Written & maintained by The Gitea Authors.
If you find a bug or want to contribute, we'll welcome you at https://gitea.com/gitea/tea.
More info about Gitea itself on https://about.gitea.com.
```
- [Compare features with other git forge CLIs](./FEATURE-COMPARISON.md)
- tea uses [code.gitea.io/sdk](https://code.gitea.io/sdk) and interacts with the Gitea API.
This project acts as a command line tool for operating one or multiple Gitea instances. It depends on [code.gitea.io/sdk](https://code.gitea.io/sdk) client SDK implementation written in Go to interact with
the Gitea API implementation.
## Installation
There are different ways to get `tea`:
Currently no prebuilt binaries are provided.
To install, a Go installation is needed.
1. Install via your system package manager:
- macOS via `brew` (official):
```sh
brew install tea
```
- 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)
- Windows via `MSYS2` ([tea](https://packages.msys2.org/base/mingw-w64-tea), thirdparty)
```sh
go get code.gitea.io/tea
go install code.gitea.io/tea
```
2. Use the prebuilt binaries from [dl.gitea.com](https://dl.gitea.com/tea/)
If the `tea` executable is not found, you might need to set up your `$GOPATH` and `$PATH` variables first:
3. Install from source: [see *Compilation*](#compilation)
```sh
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
```
4. Docker (thirdparty): [tgerczei/tea](https://hub.docker.com/r/tgerczei/tea)
If you have `brew` installed, you can install tea version via:
5. asdf (thirdparty): [mvaldes14/asdf-tea](https://github.com/mvaldes14/asdf-tea)
```sh
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
brew install --devel tea
```
## Usage
First of all, you have to create a token on your `personal settings -> application` page of your gitea instance.
Use this token to login with `tea`:
```sh
tea login add --name=try --url=https://try.gitea.io --token=xxxxxx
```
Now you can use the `tea` commands:
```sh
tea issues
tea releases
```
> If you are inside a git repository hosted on a gitea instance, you don't need to specify the `--login` and `--repo` flags!
## Compilation
Make sure you have a current go version installed (1.13 or newer).
To compile the sources yourself run the following:
- To compile the source yourself with the recommended flags & tags:
```sh
git clone https://gitea.com/gitea/tea.git # or: tea clone gitea.com/gitea/tea ;)
cd tea
make
```
Note that GNU Make (gmake on OpenBSD) is required.
If you want to install the compiled program you have to execute the following command:
```sh
make install
```
This installs the binary into the "bin" folder inside of your GOPATH folder (`go env GOPATH`). It is possible that this folder isn't in your PATH Environment Variable.
- For a quick installation without `git` & `make`, set $version and exec:
```sh
go install code.gitea.io/tea@${version}
```
```sh
go get code.gitea.io/tea
cd "${GOPATH}/src/code.gitea.io/tea"
go build
```
## Contributing
Fork -> Patch -> Push -> Pull Request
- `make test` run testsuite
- `make vet` run checks (check the order of imports; preventing failure on CI pipeline beforehand)
- ... (for other development tasks, check the `Makefile`)
## Authors
**Please** read the [CONTRIBUTING](CONTRIBUTING.md) documentation, it will tell you about internal structures and concepts.
* [Maintainers](https://github.com/orgs/go-gitea/people)
* [Contributors](https://github.com/go-gitea/tea/graphs/contributors)
## License

View File

@ -1,15 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build vendor
// +build vendor
package main
// Libraries that are included to vendor utilities used during build.
// These libraries will not be included in a normal compilation.
import (
// for vet
_ "code.gitea.io/gitea-vet"
)

View File

@ -1,54 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
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
}

View File

@ -1,53 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
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

@ -1,28 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"code.gitea.io/tea/cmd/attachments"
"code.gitea.io/tea/cmd/flags"
"github.com/urfave/cli/v2"
)
// CmdReleaseAttachments represents a release attachment (file attachment)
var CmdReleaseAttachments = cli.Command{
Name: "assets",
Aliases: []string{"asset", "a"},
Category: catEntities,
Usage: "Manage release assets",
Description: "Manage release assets",
ArgsUsage: " ", // command does not accept arguments
Action: attachments.RunReleaseAttachmentList,
Subcommands: []*cli.Command{
&attachments.CmdReleaseAttachmentList,
&attachments.CmdReleaseAttachmentCreate,
&attachments.CmdReleaseAttachmentDelete,
},
Flags: flags.AllDefaultFlags,
}

View File

@ -1,64 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package attachments
import (
"fmt"
"os"
"path/filepath"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v2"
)
// CmdReleaseAttachmentCreate represents a sub command of Release Attachments to create a release attachment
var CmdReleaseAttachmentCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create one or more release attachments",
Description: `Create one or more release attachments`,
ArgsUsage: "<release-tag> <asset> [<asset>...]",
Action: runReleaseAttachmentCreate,
Flags: flags.AllDefaultFlags,
}
func runReleaseAttachmentCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
if ctx.Args().Len() < 2 {
return fmt.Errorf("No release tag or assets specified.\nUsage:\t%s", ctx.Command.UsageText)
}
tag := ctx.Args().First()
if len(tag) == 0 {
return fmt.Errorf("Release tag needed to create attachment")
}
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
if err != nil {
return err
}
for _, asset := range ctx.Args().Slice()[1:] {
var file *os.File
if file, err = os.Open(asset); err != nil {
return err
}
filePath := filepath.Base(asset)
if _, _, err = ctx.Login.Client().CreateReleaseAttachment(ctx.Owner, ctx.Repo, release.ID, file, filePath); err != nil {
file.Close()
return err
}
file.Close()
}
return nil
}

View File

@ -1,100 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package attachments
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdReleaseAttachmentDelete represents a sub command of Release Attachments to delete a release attachment
var CmdReleaseAttachmentDelete = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "Delete one or more release attachments",
Description: `Delete one or more release attachments`,
ArgsUsage: "<release tag> <attachment name> [<attachment name>...]",
Action: runReleaseAttachmentDelete,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "confirm",
Aliases: []string{"y"},
Usage: "Confirm deletion (required)",
},
}, flags.AllDefaultFlags...),
}
func runReleaseAttachmentDelete(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
if ctx.Args().Len() < 2 {
return fmt.Errorf("No release tag or attachment names specified.\nUsage:\t%s", ctx.Command.UsageText)
}
tag := ctx.Args().First()
if len(tag) == 0 {
return fmt.Errorf("Release tag needed to delete attachment")
}
if !ctx.Bool("confirm") {
fmt.Println("Are you sure? Please confirm with -y or --confirm.")
return nil
}
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
if err != nil {
return err
}
existing, _, err := client.ListReleaseAttachments(ctx.Owner, ctx.Repo, release.ID, gitea.ListReleaseAttachmentsOptions{
ListOptions: gitea.ListOptions{Page: -1},
})
if err != nil {
return err
}
for _, name := range ctx.Args().Slice()[1:] {
var attachment *gitea.Attachment
for _, a := range existing {
if a.Name == name {
attachment = a
}
}
if attachment == nil {
return fmt.Errorf("Release does not have attachment named '%s'", name)
}
_, err = client.DeleteReleaseAttachment(ctx.Owner, ctx.Repo, release.ID, attachment.ID)
if err != nil {
return err
}
}
return nil
}
func getReleaseAttachmentByName(owner, repo string, release int64, name string, client *gitea.Client) (*gitea.Attachment, error) {
al, _, err := client.ListReleaseAttachments(owner, repo, release, gitea.ListReleaseAttachmentsOptions{
ListOptions: gitea.ListOptions{Page: -1},
})
if err != nil {
return nil, err
}
if len(al) == 0 {
return nil, fmt.Errorf("Release does not have any attachments")
}
for _, a := range al {
if a.Name == name {
return a, nil
}
}
return nil, fmt.Errorf("Attachment does not exist")
}

View File

@ -1,74 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package attachments
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"
)
// CmdReleaseAttachmentList represents a sub command of release attachment to list release attachments
var CmdReleaseAttachmentList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List Release Attachments",
Description: "List Release Attachments",
ArgsUsage: "<release-tag>", // command does not accept arguments
Action: RunReleaseAttachmentList,
Flags: append([]cli.Flag{
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
// RunReleaseAttachmentList list release attachments
func RunReleaseAttachmentList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
tag := ctx.Args().First()
if len(tag) == 0 {
return fmt.Errorf("Release tag needed to list attachments")
}
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
if err != nil {
return err
}
attachments, _, err := ctx.Login.Client().ListReleaseAttachments(ctx.Owner, ctx.Repo, release.ID, gitea.ListReleaseAttachmentsOptions{
ListOptions: ctx.GetListOptions(),
})
if err != nil {
return err
}
print.ReleaseAttachmentsList(attachments, ctx.Output)
return nil
}
func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) {
rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{
ListOptions: gitea.ListOptions{Page: -1},
})
if err != nil {
return nil, err
}
if len(rl) == 0 {
return nil, fmt.Errorf("Repo does not have any release")
}
for _, r := range rl {
if r.TagName == tag {
return r, nil
}
}
return nil, fmt.Errorf("Release tag does not exist")
}

View File

@ -1,137 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"fmt"
"io"
"net/http"
"os"
"os/exec"
"github.com/adrg/xdg"
"github.com/urfave/cli/v2"
)
// CmdAutocomplete manages autocompletion
var CmdAutocomplete = cli.Command{
Name: "shellcompletion",
Aliases: []string{"autocomplete"},
Category: catSetup,
Usage: "Install shell completion for tea",
Description: "Install shell completion for tea",
ArgsUsage: "<shell type> (bash, zsh, powershell, fish)",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "install",
Usage: "Persist in shell config instead of printing commands",
},
},
Action: runAutocompleteAdd,
}
func runAutocompleteAdd(ctx *cli.Context) error {
var remoteFile, localFile, cmds string
shell := ctx.Args().First()
switch shell {
case "zsh":
remoteFile = "contrib/autocomplete.zsh"
localFile = "autocomplete.zsh"
cmds = "echo 'PROG=tea _CLI_ZSH_AUTOCOMPLETE_HACK=1 source \"%s\"' >> ~/.zshrc && source ~/.zshrc"
case "bash":
remoteFile = "contrib/autocomplete.sh"
localFile = "autocomplete.sh"
cmds = "echo 'PROG=tea source \"%s\"' >> ~/.bashrc && source ~/.bashrc"
case "powershell":
remoteFile = "contrib/autocomplete.ps1"
localFile = "tea.ps1"
cmds = "\"& %s\" >> $profile"
case "fish":
// fish is different, in that urfave/cli provides a generator for the shell script needed.
// this also means that the fish completion can become out of sync with the tea binary!
// writing to this directory suffices, as fish reads files there on startup, no cmds needed.
return writeFishAutoCompleteFile(ctx)
default:
return fmt.Errorf("Must specify valid %s", ctx.Command.ArgsUsage)
}
localPath, err := xdg.ConfigFile("tea/" + localFile)
if err != nil {
return err
}
cmds = fmt.Sprintf(cmds, localPath)
if err = writeRemoteAutoCompleteFile(remoteFile, localPath); err != nil {
return err
}
if ctx.Bool("install") {
fmt.Println("Installing in your shellrc")
installer := exec.Command(shell, "-c", cmds)
if shell == "powershell" {
installer = exec.Command("powershell.exe", "-Command", cmds)
}
out, err := installer.CombinedOutput()
if err != nil {
return fmt.Errorf("Couldn't run the commands: %s %s", err, out)
}
} else {
fmt.Println("\n# Run the following commands to install autocompletion (or use --install)")
fmt.Println(cmds)
}
return nil
}
func writeRemoteAutoCompleteFile(file, destPath string) error {
url := fmt.Sprintf("https://gitea.com/gitea/tea/raw/branch/master/%s", file)
fmt.Println("Fetching " + url)
res, err := http.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
writer, err := os.Create(destPath)
if err != nil {
return err
}
defer writer.Close()
_, err = io.Copy(writer, res.Body)
return err
}
func writeFishAutoCompleteFile(ctx *cli.Context) error {
// NOTE: to make sure this file is in sync with tea commands, we'd need to
// - check if the file exists
// - if it does, check if the tea version that wrote it is the currently running version
// - if not, rewrite the file
// on each application run
// NOTE: this generates a completion that also suggests file names, which looks kinda messy..
script, err := ctx.App.ToFishCompletion()
if err != nil {
return err
}
localPath, err := xdg.ConfigFile("fish/conf.d/tea_completion.fish")
if err != nil {
return err
}
writer, err := os.Create(localPath)
if err != nil {
return err
}
if _, err = io.WriteString(writer, script); err != nil {
return err
}
fmt.Printf("Installed tab completion to %s\n", localPath)
return nil
}

View File

@ -1,36 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"code.gitea.io/tea/cmd/branches"
"github.com/urfave/cli/v2"
)
// CmdBranches represents to login a gitea server.
var CmdBranches = cli.Command{
Name: "branches",
Aliases: []string{"branch", "b"},
Category: catEntities,
Usage: "Consult branches",
Description: `Lists branches when called without argument. If a branch is provided, will show it in detail.`,
ArgsUsage: "[<branch name>]",
Action: runBranches,
Subcommands: []*cli.Command{
&branches.CmdBranchesList,
&branches.CmdBranchesProtect,
&branches.CmdBranchesUnprotect,
},
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "comments",
Usage: "Whether to display comments (will prompt if not provided & run interactively)",
},
}, branches.CmdBranchesList.Flags...),
}
func runBranches(ctx *cli.Context) error {
return branches.RunBranchesList(ctx)
}

View File

@ -1,73 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package branches
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 branchFieldsFlag = flags.FieldsFlag(print.BranchFields, []string{
"name", "protected", "user-can-merge", "user-can-push",
})
// CmdBranchesListFlags Flags for command list
var CmdBranchesListFlags = append([]cli.Flag{
branchFieldsFlag,
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...)
// CmdBranchesList represents a sub command of branches to list branches
var CmdBranchesList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List branches of the repository",
Description: `List branches of the repository`,
ArgsUsage: " ", // command does not accept arguments
Action: RunBranchesList,
Flags: CmdBranchesListFlags,
}
// RunBranchesList list branches
func RunBranchesList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
owner := ctx.Owner
if ctx.IsSet("owner") {
owner = ctx.String("owner")
}
var branches []*gitea.Branch
var protections []*gitea.BranchProtection
var err error
branches, _, err = ctx.Login.Client().ListRepoBranches(owner, ctx.Repo, gitea.ListRepoBranchesOptions{
ListOptions: ctx.GetListOptions(),
})
if err != nil {
return err
}
protections, _, err = ctx.Login.Client().ListBranchProtections(owner, ctx.Repo, gitea.ListBranchProtectionsOptions{
ListOptions: ctx.GetListOptions(),
})
if err != nil {
return err
}
fields, err := branchFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
print.BranchesList(branches, protections, ctx.Output, fields)
return nil
}

View File

@ -1,101 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package branches
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdBranchesProtectFlags Flags for command protect/unprotect
var CmdBranchesProtectFlags = append([]cli.Flag{
branchFieldsFlag,
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...)
// CmdBranchesProtect represents a sub command of branches to protect a branch
var CmdBranchesProtect = cli.Command{
Name: "protect",
Aliases: []string{"P"},
Usage: "Protect branches",
Description: `Block actions push/merge on specified branches`,
ArgsUsage: "<branch>",
Action: RunBranchesProtect,
Flags: CmdBranchesProtectFlags,
}
// CmdBranchesUnprotect represents a sub command of branches to protect a branch
var CmdBranchesUnprotect = cli.Command{
Name: "unprotect",
Aliases: []string{"U"},
Usage: "Unprotect branches",
Description: `Suppress existing protections on specified branches`,
ArgsUsage: "<branch>",
Action: RunBranchesProtect,
Flags: CmdBranchesProtectFlags,
}
// RunBranchesProtect function to protect/unprotect a list of branches
func RunBranchesProtect(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if !cmd.Args().Present() {
return fmt.Errorf("must specify at least one branch")
}
owner := ctx.Owner
if ctx.IsSet("owner") {
owner = ctx.String("owner")
}
for _, branch := range ctx.Args().Slice() {
var err error
command := ctx.Command.Name
if command == "protect" {
_, _, err = ctx.Login.Client().CreateBranchProtection(owner, ctx.Repo, gitea.CreateBranchProtectionOption{
BranchName: branch,
RuleName: "",
EnablePush: false,
EnablePushWhitelist: false,
PushWhitelistUsernames: []string{},
PushWhitelistTeams: []string{},
PushWhitelistDeployKeys: false,
EnableMergeWhitelist: false,
MergeWhitelistUsernames: []string{},
MergeWhitelistTeams: []string{},
EnableStatusCheck: false,
StatusCheckContexts: []string{},
RequiredApprovals: 1,
EnableApprovalsWhitelist: false,
ApprovalsWhitelistUsernames: []string{},
ApprovalsWhitelistTeams: []string{},
BlockOnRejectedReviews: false,
BlockOnOfficialReviewRequests: false,
BlockOnOutdatedBranch: false,
DismissStaleApprovals: false,
RequireSignedCommits: false,
ProtectedFilePatterns: "",
UnprotectedFilePatterns: "",
})
} else if command == "unprotect" {
_, err = ctx.Login.Client().DeleteBranchProtection(owner, ctx.Repo, branch)
} else {
return fmt.Errorf("command %s is not supported", command)
}
if err != nil {
return err
}
}
return nil
}

View File

@ -1,11 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
var (
catSetup = "SETUP"
catEntities = "ENTITIES"
catHelpers = "HELPERS"
catMisc = "MISCELLANEOUS"
)

View File

@ -1,88 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
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

@ -1,82 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"fmt"
"io"
"strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
)
// CmdAddComment is the main command to operate with notifications
var CmdAddComment = cli.Command{
Name: "comment",
Aliases: []string{"c"},
Category: catEntities,
Usage: "Add a comment to an issue / pr",
Description: "Add a comment to an issue / pr",
ArgsUsage: "<issue / pr index> [<comment body>]",
Action: runAddComment,
Flags: flags.AllDefaultFlags,
}
func runAddComment(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
args := ctx.Args()
if args.Len() == 0 {
return fmt.Errorf("Please specify issue / pr index")
}
idx, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
body := strings.Join(ctx.Args().Tail(), " ")
if interact.IsStdinPiped() {
// custom solution until https://github.com/AlecAivazis/survey/issues/328 is fixed
if bodyStdin, err := io.ReadAll(ctx.App.Reader); err != nil {
return err
} else if len(bodyStdin) != 0 {
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
}
} else if len(body) == 0 {
if err = survey.AskOne(interact.NewMultiline(interact.Multiline{
Message: "Comment:",
Syntax: "md",
UseEditor: config.GetPreferences().Editor,
}), &body); err != nil {
return err
}
}
if len(body) == 0 {
return fmt.Errorf("No comment body provided")
}
client := ctx.Login.Client()
comment, _, err := client.CreateIssueComment(ctx.Owner, ctx.Repo, idx, gitea.CreateIssueCommentOption{
Body: body,
})
if err != nil {
return err
}
print.Comment(comment)
return nil
}

246
cmd/config.go Normal file
View File

@ -0,0 +1,246 @@
// Copyright 2018 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 (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"path/filepath"
"strings"
"code.gitea.io/sdk/gitea"
local_git "code.gitea.io/tea/modules/git"
"code.gitea.io/tea/modules/utils"
go_git "gopkg.in/src-d/go-git.v4"
"github.com/go-gitea/yaml"
)
// Login represents a login to a gitea server, you even could add multiple logins for one gitea server
type Login struct {
Name string `yaml:"name"`
URL string `yaml:"url"`
Token string `yaml:"token"`
Active bool `yaml:"active"`
SSHHost string `yaml:"ssh_host"`
Insecure bool `yaml:"insecure"`
}
// Client returns a client to operate Gitea API
func (l *Login) Client() *gitea.Client {
client := gitea.NewClient(l.URL, l.Token)
if l.Insecure {
cookieJar, _ := cookiejar.New(nil)
client.SetHTTPClient(&http.Client{
Jar: cookieJar,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
})
}
return client
}
// GetSSHHost returns SSH host name
func (l *Login) GetSSHHost() string {
if l.SSHHost != "" {
return l.SSHHost
}
u, err := url.Parse(l.URL)
if err != nil {
return ""
}
return u.Hostname()
}
// Config reprensents local configurations
type Config struct {
Logins []Login `yaml:"logins"`
}
var (
config Config
yamlConfigPath string
)
func init() {
homeDir, err := utils.Home()
if err != nil {
log.Fatal("Retrieve home dir failed")
}
dir := filepath.Join(homeDir, ".tea")
err = os.MkdirAll(dir, os.ModePerm)
if err != nil {
log.Fatal("Init tea config dir " + dir + " failed")
}
yamlConfigPath = filepath.Join(dir, "tea.yml")
}
func splitRepo(repoPath string) (string, string) {
p := strings.Split(repoPath, "/")
if len(p) >= 2 {
return p[0], p[1]
}
return repoPath, ""
}
func getActiveLogin() (*Login, error) {
if len(config.Logins) == 0 {
return nil, errors.New("No available login")
}
for _, l := range config.Logins {
if l.Active {
return &l, nil
}
}
return &config.Logins[0], nil
}
func getLoginByName(name string) *Login {
for _, l := range config.Logins {
if l.Name == name {
return &l
}
}
return nil
}
func addLogin(login Login) error {
for _, l := range config.Logins {
if l.Name == login.Name {
if l.URL == login.URL && l.Token == login.Token {
return nil
}
return errors.New("Login name has already been used")
}
if l.URL == login.URL && l.Token == login.Token {
return errors.New("URL has been added")
}
}
u, err := url.Parse(login.URL)
if err != nil {
return err
}
if login.SSHHost == "" {
login.SSHHost = u.Hostname()
}
config.Logins = append(config.Logins, login)
return nil
}
func isFileExist(fileName string) (bool, error) {
f, err := os.Stat(fileName)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
if f.IsDir() {
return false, errors.New("A directory with the same name exists")
}
return true, nil
}
func loadConfig(ymlPath string) error {
exist, _ := isFileExist(ymlPath)
if exist {
Println("Found config file", ymlPath)
bs, err := ioutil.ReadFile(ymlPath)
if err != nil {
return err
}
err = yaml.Unmarshal(bs, &config)
if err != nil {
return err
}
}
return nil
}
func saveConfig(ymlPath string) error {
bs, err := yaml.Marshal(&config)
if err != nil {
return err
}
return ioutil.WriteFile(ymlPath, bs, 0660)
}
func curGitRepoPath() (*Login, string, error) {
gitPath, err := go_git.PlainOpenWithOptions("./", &go_git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
return nil, "", errors.New("No Gitea login found")
}
gitConfig, err := gitPath.Config()
if err != nil {
return nil, "", err
}
// if no remote
if len(gitConfig.Remotes) == 0 {
return nil, "", errors.New("No remote(s) found in this Git repository")
}
// if only one remote exists
if len(gitConfig.Remotes) >= 1 && len(remoteValue) == 0 {
for remote := range gitConfig.Remotes {
remoteValue = remote
}
if len(gitConfig.Remotes) > 1 {
// if master branch is present, use it as the default remote
masterBranch, ok := gitConfig.Branches["master"]
if ok {
if len(masterBranch.Remote) > 0 {
remoteValue = masterBranch.Remote
}
}
}
}
remoteConfig, ok := gitConfig.Remotes[remoteValue]
if !ok || remoteConfig == nil {
return nil, "", errors.New("Remote " + remoteValue + " not found in this Git repository")
}
for _, l := range config.Logins {
for _, u := range remoteConfig.URLs {
p, err := local_git.ParseURL(strings.TrimSpace(u))
if err != nil {
return nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error())
}
if strings.EqualFold(p.Scheme, "http") || strings.EqualFold(p.Scheme, "https") {
if strings.HasPrefix(u, l.URL) {
ps := strings.Split(p.Path, "/")
path := strings.Join(ps[len(ps)-2:], "/")
return &l, strings.TrimSuffix(path, ".git"), nil
}
} else if strings.EqualFold(p.Scheme, "ssh") {
if l.GetSSHHost() == strings.Split(p.Host, ":")[0] {
return &l, strings.TrimLeft(strings.TrimSuffix(p.Path, ".git"), "/"), nil
}
}
}
}
return nil, "", errors.New("No Gitea login found. You might want to specify --repo (and --login) to work outside of a repository")
}

View File

@ -1,55 +0,0 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"fmt"
"os"
"path/filepath"
"github.com/urfave/cli/v2"
)
// CmdDocs generates markdown for tea
var CmdDocs = cli.Command{
Name: "docs",
Hidden: true,
Description: "Generate CLI docs",
Action: runDocs,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "out",
Usage: "Path to output docs to, otherwise prints to stdout",
Aliases: []string{"o"},
},
},
}
func runDocs(ctx *cli.Context) error {
md, err := ctx.App.ToMarkdown()
if err != nil {
return err
}
outPath := ctx.String("out")
if outPath == "" {
fmt.Print(md)
return nil
}
if err := os.MkdirAll(filepath.Dir(outPath), os.ModePerm); err != nil {
return err
}
fi, err := os.Create(outPath)
if err != nil {
return err
}
defer fi.Close()
if _, err := fi.WriteString(md); err != nil {
return err
}
return nil
}

124
cmd/flags.go Normal file
View File

@ -0,0 +1,124 @@
// 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 cmd
import (
"log"
"github.com/urfave/cli"
)
// create global variables for global Flags to simplify
// access to the options without requiring cli.Context
var (
loginValue string
repoValue string
outputValue string
remoteValue string
)
// LoginFlag provides flag to specify tea login profile
var LoginFlag = cli.StringFlag{
Name: "login, l",
Usage: "Use a different Gitea login. Optional",
Destination: &loginValue,
}
// RepoFlag provides flag to specify repository
var RepoFlag = cli.StringFlag{
Name: "repo, r",
Usage: "Repository to interact with. Optional",
Destination: &repoValue,
}
// RemoteFlag provides flag to specify remote repository
var RemoteFlag = cli.StringFlag{
Name: "remote, R",
Usage: "Discover Gitea login from remote. Optional",
Destination: &remoteValue,
}
// OutputFlag provides flag to specify output type
var OutputFlag = cli.StringFlag{
Name: "output, o",
Usage: "Output format. (csv, simple, table, tsv, yaml)",
Destination: &outputValue,
}
// LoginOutputFlags defines login and output flags that should
// added to all subcommands and appended to the flags of the
// subcommand to work around issue and provide --login and --output:
// https://github.com/urfave/cli/issues/585
var LoginOutputFlags = []cli.Flag{
LoginFlag,
OutputFlag,
}
// LoginRepoFlags defines login and repo flags that should
// be used for all subcommands and appended to the flags of
// the subcommand to work around issue and provide --login and --repo:
// https://github.com/urfave/cli/issues/585
var LoginRepoFlags = []cli.Flag{
LoginFlag,
RepoFlag,
}
// AllDefaultFlags defines flags that should be available
// for all subcommands working with dedicated repositories
// to work around issue and provide --login, --repo and --output:
// https://github.com/urfave/cli/issues/585
var AllDefaultFlags = append([]cli.Flag{
RepoFlag,
RemoteFlag,
}, LoginOutputFlags...)
// initCommand returns repository and *Login based on flags
func initCommand() (*Login, string, string) {
err := loadConfig(yamlConfigPath)
if err != nil {
log.Fatal("Unable to load config file " + yamlConfigPath)
}
var login *Login
if loginValue == "" {
login, err = getActiveLogin()
if err != nil {
log.Fatal(err)
}
} else {
login = getLoginByName(loginValue)
if login == nil {
log.Fatal("Login name " + loginValue + " does not exist")
}
}
repoPath := repoValue
if repoPath == "" {
login, repoPath, err = curGitRepoPath()
if err != nil {
log.Fatal(err.Error())
}
}
owner, repo := splitRepo(repoPath)
return login, owner, repo
}
// initCommandLoginOnly return *Login based on flags
func initCommandLoginOnly() *Login {
err := loadConfig(yamlConfigPath)
if err != nil {
log.Fatal("load config file failed ", yamlConfigPath)
}
var login *Login
login = getLoginByName(loginValue)
if login == nil {
log.Fatal("indicated login name ", loginValue, " does not exist")
}
return login
}

View File

@ -1,52 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package flags
import (
"fmt"
"strings"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CsvFlag is a wrapper around cli.StringFlag, with an added GetValues() method
// to retrieve comma separated string values as a slice.
type CsvFlag struct {
cli.StringFlag
AvailableFields []string
}
// NewCsvFlag creates a CsvFlag, while setting its usage string and default values
func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag {
var availableDesc string
if len(availableValues) != 0 {
availableDesc = " Available values:"
}
return &CsvFlag{
AvailableFields: availableValues,
StringFlag: cli.StringFlag{
Name: name,
Aliases: aliases,
Value: strings.Join(defaults, ","),
Usage: fmt.Sprintf(`Comma-separated list of %s.%s
%s
`, usage, availableDesc, strings.Join(availableValues, ",")),
},
}
}
// GetValues returns the value of the flag, parsed as a commaseparated list
func (f CsvFlag) GetValues(ctx *cli.Context) ([]string, error) {
val := ctx.String(f.Name)
selection := strings.Split(val, ",")
if f.AvailableFields != nil && val != "" {
for _, field := range selection {
if !utils.Contains(f.AvailableFields, field) {
return nil, fmt.Errorf("Invalid field '%s'", field)
}
}
}
return selection, nil
}

View File

@ -1,105 +0,0 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package flags
import (
"github.com/urfave/cli/v2"
)
// LoginFlag provides flag to specify tea login profile
var LoginFlag = cli.StringFlag{
Name: "login",
Aliases: []string{"l"},
Usage: "Use a different Gitea Login. Optional",
}
// RepoFlag provides flag to specify repository
var RepoFlag = cli.StringFlag{
Name: "repo",
Aliases: []string{"r"},
Usage: "Override local repository path or gitea repository slug to interact with. Optional",
}
// RemoteFlag provides flag to specify remote repository
var RemoteFlag = cli.StringFlag{
Name: "remote",
Aliases: []string{"R"},
Usage: "Discover Gitea login from remote. Optional",
}
// OutputFlag provides flag to specify output type
var OutputFlag = cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "Output format. (simple, table, csv, tsv, yaml, json)",
}
// PaginationPageFlag provides flag for pagination options
var PaginationPageFlag = cli.StringFlag{
Name: "page",
Aliases: []string{"p"},
Usage: "specify page, default is 1",
}
// PaginationLimitFlag provides flag for pagination options
var PaginationLimitFlag = cli.StringFlag{
Name: "limit",
Aliases: []string{"lm"},
Usage: "specify limit of items per page",
}
// LoginOutputFlags defines login and output flags that should
// added to all subcommands and appended to the flags of the
// subcommand to work around issue and provide --login and --output:
// https://github.com/urfave/cli/issues/585
var LoginOutputFlags = []cli.Flag{
&LoginFlag,
&OutputFlag,
}
// LoginRepoFlags defines login and repo flags that should
// be used for all subcommands and appended to the flags of
// the subcommand to work around issue and provide --login and --repo:
// https://github.com/urfave/cli/issues/585
var LoginRepoFlags = []cli.Flag{
&LoginFlag,
&RepoFlag,
&RemoteFlag,
}
// AllDefaultFlags defines flags that should be available
// for all subcommands working with dedicated repositories
// to work around issue and provide --login, --repo and --output:
// https://github.com/urfave/cli/issues/585
var AllDefaultFlags = append([]cli.Flag{
&RepoFlag,
&RemoteFlag,
}, LoginOutputFlags...)
// NotificationFlags defines flags that should be available on notifications.
var NotificationFlags = append([]cli.Flag{
NotificationStateFlag,
&cli.BoolFlag{
Name: "mine",
Aliases: []string{"m"},
Usage: "Show notifications across all your repositories instead of the current repository only",
},
&PaginationPageFlag,
&PaginationLimitFlag,
}, AllDefaultFlags...)
// NotificationStateFlag is a csv flag applied to all notification subcommands as filter
var NotificationStateFlag = NewCsvFlag(
"states",
"notification states to filter by",
[]string{"s"},
[]string{"pinned", "unread", "read"},
[]string{"unread", "pinned"},
)
// FieldsFlag generates a flag selecting printable fields.
// To retrieve the value, use f.GetValues()
func FieldsFlag(availableFields, defaultFields []string) *CsvFlag {
return NewCsvFlag("fields", "fields to print", []string{"f"}, availableFields, defaultFields)
}

View File

@ -1,238 +0,0 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package flags
import (
"fmt"
"strings"
"time"
"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: "owner",
Aliases: []string{"org"},
},
&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...)
// issuePRFlags defines shared flags between flags IssuePRCreateFlags and IssuePREditFlags
var issuePRFlags = append([]cli.Flag{
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"d"},
},
&cli.StringFlag{
Name: "referenced-version",
Aliases: []string{"v"},
Usage: "commit-hash or tag name to assign",
},
&cli.StringFlag{
Name: "milestone",
Aliases: []string{"m"},
Usage: "Milestone to assign",
},
&cli.StringFlag{
Name: "deadline",
Aliases: []string{"D"},
Usage: "Deadline timestamp to assign",
},
}, LoginRepoFlags...)
// IssuePRCreateFlags defines flags for creation of issues and PRs
var IssuePRCreateFlags = append([]cli.Flag{
&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",
},
}, issuePRFlags...)
// GetIssuePRCreateFlags parses all IssuePREditFlags
func GetIssuePRCreateFlags(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
}
// IssuePREditFlags defines flags for editing properties of issues and PRs
var IssuePREditFlags = append([]cli.Flag{
&cli.StringFlag{
Name: "add-assignees",
Aliases: []string{"a"},
Usage: "Comma-separated list of usernames to assign",
},
&cli.StringFlag{
Name: "add-labels",
Aliases: []string{"L"},
Usage: "Comma-separated list of labels to assign. Takes precedence over --remove-labels",
},
&cli.StringFlag{
Name: "remove-labels",
Usage: "Comma-separated list of labels to remove",
},
}, issuePRFlags...)
// GetIssuePREditFlags parses all IssuePREditFlags
func GetIssuePREditFlags(ctx *context.TeaContext) (*task.EditIssueOption, error) {
opts := task.EditIssueOption{}
if ctx.IsSet("title") {
val := ctx.String("title")
opts.Title = &val
}
if ctx.IsSet("description") {
val := ctx.String("description")
opts.Body = &val
}
if ctx.IsSet("referenced-version") {
val := ctx.String("referenced-version")
opts.Ref = &val
}
if ctx.IsSet("milestone") {
val := ctx.String("milestone")
opts.Milestone = &val
}
if ctx.IsSet("deadline") {
date := ctx.String("deadline")
if date == "" {
opts.Deadline = &time.Time{}
} else {
t, err := dateparse.ParseAny(date)
if err != nil {
return nil, err
}
opts.Deadline = &t
}
}
if ctx.IsSet("add-assignees") {
val := ctx.String("add-assignees")
opts.AddAssignees = strings.Split(val, ",")
}
if ctx.IsSet("add-labels") {
val := ctx.String("add-labels")
opts.AddLabels = strings.Split(val, ",")
}
if ctx.IsSet("remove-labels") {
val := ctx.String("remove-labels")
opts.RemoveLabels = strings.Split(val, ",")
}
return &opts, nil
}

View File

@ -1,75 +1,157 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"fmt"
"log"
"os"
"strconv"
"strings"
"code.gitea.io/tea/cmd/issues"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
"github.com/urfave/cli"
)
// CmdIssues represents to login a gitea server.
var CmdIssues = cli.Command{
Name: "issues",
Aliases: []string{"issue", "i"},
Category: catEntities,
Usage: "List, create and update issues",
Description: `Lists issues when called without argument. If issue index is provided, will show it in detail.`,
ArgsUsage: "[<issue index>]",
Usage: "List and create issues",
Description: `List and create issues`,
Action: runIssues,
Subcommands: []*cli.Command{
&issues.CmdIssuesList,
&issues.CmdIssuesCreate,
&issues.CmdIssuesEdit,
&issues.CmdIssuesReopen,
&issues.CmdIssuesClose,
Subcommands: []cli.Command{
CmdIssuesList,
CmdIssuesCreate,
},
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "comments",
Usage: "Whether to display comments (will prompt if not provided & run interactively)",
},
}, issues.CmdIssuesList.Flags...),
Flags: AllDefaultFlags,
}
// CmdIssuesList represents a sub command of issues to list issues
var CmdIssuesList = cli.Command{
Name: "ls",
Usage: "List issues of the repository",
Description: `List issues of the repository`,
Action: runIssuesList,
Flags: AllDefaultFlags,
}
func runIssues(ctx *cli.Context) error {
if ctx.Args().Len() == 1 {
return runIssueDetail(ctx, ctx.Args().First())
if len(os.Args) == 3 {
return runIssueDetail(ctx, os.Args[2])
}
return issues.RunIssuesList(ctx)
return runIssuesList(ctx)
}
func runIssueDetail(cmd *cli.Context, index string) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
func runIssueDetail(ctx *cli.Context, index string) error {
login, owner, repo := initCommand()
idx, err := utils.ArgToIndex(index)
if err != nil {
return err
if strings.HasPrefix(index, "#") {
index = index[1:]
}
client := ctx.Login.Client()
issue, _, err := client.GetIssue(ctx.Owner, ctx.Repo, idx)
if err != nil {
return err
}
reactions, _, err := client.GetIssueReactions(ctx.Owner, ctx.Repo, idx)
if err != nil {
return err
}
print.IssueDetails(issue, reactions)
if issue.Comments > 0 {
err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments)
if err != nil {
return fmt.Errorf("error loading comments: %v", err)
idx, err := strconv.ParseInt(index, 10, 64)
if err != nil {
return err
}
issue, err := login.Client().GetIssue(owner, repo, idx)
if err != nil {
return err
}
fmt.Printf("#%d %s\n%s created %s\n\n%s", issue.Index,
issue.Title,
issue.Poster.UserName,
issue.Created.Format("2006-01-02 15:04:05"),
issue.Body,
)
return nil
}
func runIssuesList(ctx *cli.Context) error {
login, owner, repo := initCommand()
issues, err := login.Client().ListRepoIssues(owner, repo, gitea.ListIssueOption{
Page: 0,
State: string(gitea.StateOpen),
})
if err != nil {
log.Fatal(err)
}
headers := []string{
"Index",
"Name",
"Updated",
"Title",
}
var values [][]string
if len(issues) == 0 {
Output(outputValue, headers, values)
return nil
}
for _, issue := range issues {
name := issue.Poster.FullName
if len(name) == 0 {
name = issue.Poster.UserName
}
values = append(
values,
[]string{
strconv.FormatInt(issue.Index, 10),
name,
issue.Updated.Format("2006-01-02 15:04:05"),
issue.Title,
},
)
}
Output(outputValue, headers, values)
return nil
}
// CmdIssuesCreate represents a sub command of issues to create issue
var CmdIssuesCreate = cli.Command{
Name: "create",
Usage: "Create an issue on repository",
Description: `Create an issue on repository`,
Action: runIssuesCreate,
Flags: append([]cli.Flag{
cli.StringFlag{
Name: "title, t",
Usage: "issue title to create",
},
cli.StringFlag{
Name: "body, b",
Usage: "issue body to create",
},
}, LoginRepoFlags...),
}
func runIssuesCreate(ctx *cli.Context) error {
login, owner, repo := initCommand()
_, err := login.Client().CreateIssue(owner, repo, gitea.CreateIssueOption{
Title: ctx.String("title"),
Body: ctx.String("body"),
// TODO:
//Assignee string `json:"assignee"`
//Assignees []string `json:"assignees"`
//Deadline *time.Time `json:"due_date"`
//Milestone int64 `json:"milestone"`
//Labels []int64 `json:"labels"`
//Closed bool `json:"closed"`
})
if err != nil {
log.Fatal(err)
}
return nil

View File

@ -1,58 +0,0 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
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"
)
// CmdIssuesClose represents a sub command of issues to close an issue
var CmdIssuesClose = cli.Command{
Name: "close",
Usage: "Change state of one ore more issues to 'closed'",
Description: `Change state of one ore more issues to 'closed'`,
ArgsUsage: "<issue index> [<issue index>...]",
Action: func(ctx *cli.Context) error {
var s = gitea.StateClosed
return editIssueState(ctx, gitea.EditIssueOption{State: &s})
},
Flags: flags.AllDefaultFlags,
}
// editIssueState abstracts the arg parsing to edit the given issue
func editIssueState(cmd *cli.Context, opts gitea.EditIssueOption) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() == 0 {
return fmt.Errorf(ctx.Command.ArgsUsage)
}
indices, err := utils.ArgsToIndices(ctx.Args().Slice())
if err != nil {
return err
}
client := ctx.Login.Client()
for _, index := range indices {
issue, _, err := client.EditIssue(ctx.Owner, ctx.Repo, index, opts)
if err != nil {
return err
}
if len(indices) > 1 {
fmt.Println(issue.HTMLURL)
} else {
print.IssueDetails(issue, nil)
}
}
return nil
}

View File

@ -1,45 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task"
"github.com/urfave/cli/v2"
)
// CmdIssuesCreate represents a sub command of issues to create issue
var CmdIssuesCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create an issue on repository",
Description: `Create an issue on repository`,
ArgsUsage: " ", // command does not accept arguments
Action: runIssuesCreate,
Flags: flags.IssuePRCreateFlags,
}
func runIssuesCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.NumFlags() == 0 {
return interact.CreateIssue(ctx.Login, ctx.Owner, ctx.Repo)
}
opts, err := flags.GetIssuePRCreateFlags(ctx)
if err != nil {
return err
}
return task.CreateIssue(
ctx.Login,
ctx.Owner,
ctx.Repo,
*opts,
)
}

View File

@ -1,62 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
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/task"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CmdIssuesEdit is the subcommand of issues to edit issues
var CmdIssuesEdit = cli.Command{
Name: "edit",
Aliases: []string{"e"},
Usage: "Edit one or more issues",
Description: `Edit one or more issues. To unset a property again,
use an empty string (eg. --milestone "").`,
ArgsUsage: "<idx> [<idx>...]",
Action: runIssuesEdit,
Flags: flags.IssuePREditFlags,
}
func runIssuesEdit(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if !cmd.Args().Present() {
return fmt.Errorf("must specify at least one issue index")
}
opts, err := flags.GetIssuePREditFlags(ctx)
if err != nil {
return err
}
indices, err := utils.ArgsToIndices(ctx.Args().Slice())
if err != nil {
return err
}
client := ctx.Login.Client()
for _, opts.Index = range indices {
issue, err := task.EditIssue(ctx, client, *opts)
if err != nil {
return err
}
if ctx.Args().Len() > 1 {
fmt.Println(issue.HTMLURL)
} else {
print.IssueDetails(issue, nil)
}
}
return nil
}

View File

@ -1,131 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
import (
"fmt"
"time"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea"
"github.com/araddon/dateparse"
"github.com/urfave/cli/v2"
)
var issueFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{
"index", "title", "state", "author", "milestone", "labels", "owner", "repo",
})
// CmdIssuesList represents a sub command of issues to list issues
var CmdIssuesList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List issues of the repository",
Description: `List issues of the repository`,
ArgsUsage: " ", // command does not accept arguments
Action: RunIssuesList,
Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssueListingFlags...),
}
// RunIssuesList list issues
func RunIssuesList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
state := gitea.StateOpen
switch ctx.String("state") {
case "all":
state = gitea.StateAll
case "", "open":
state = gitea.StateOpen
case "closed":
state = gitea.StateClosed
default:
return fmt.Errorf("unknown state '%s'", ctx.String("state"))
}
kind := gitea.IssueTypeIssue
switch ctx.String("kind") {
case "", "issues", "issue":
kind = gitea.IssueTypeIssue
case "pulls", "pull", "pr":
kind = gitea.IssueTypePull
case "all":
kind = gitea.IssueTypeAll
default:
return fmt.Errorf("unknown kind '%s'", ctx.String("kind"))
}
var err error
var from, until time.Time
if ctx.IsSet("from") {
from, err = dateparse.ParseLocal(ctx.String("from"))
if err != nil {
return err
}
}
if ctx.IsSet("until") {
until, err = dateparse.ParseLocal(ctx.String("until"))
if err != nil {
return err
}
}
owner := ctx.Owner
if ctx.IsSet("owner") {
owner = ctx.String("owner")
}
// ignore error, as we don't do any input validation on these flags
labels, _ := flags.LabelFilterFlag.GetValues(cmd)
milestones, _ := flags.MilestoneFilterFlag.GetValues(cmd)
var issues []*gitea.Issue
if ctx.Repo != "" {
issues, _, err = ctx.Login.Client().ListRepoIssues(owner, ctx.Repo, gitea.ListIssueOption{
ListOptions: ctx.GetListOptions(),
State: state,
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 {
return err
}
} else {
issues, _, err = ctx.Login.Client().ListIssues(gitea.ListIssueOption{
ListOptions: ctx.GetListOptions(),
State: state,
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,
Owner: owner,
})
if err != nil {
return err
}
}
fields, err := issueFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
print.IssuesPullsList(issues, ctx.Output, fields)
return nil
}

View File

@ -1,25 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdIssuesReopen represents a sub command of issues to open an issue
var CmdIssuesReopen = cli.Command{
Name: "reopen",
Aliases: []string{"open"},
Usage: "Change state of one or more issues to 'open'",
Description: `Change state of one or more issues to 'open'`,
ArgsUsage: "<issue index> [<issue index>...]",
Action: func(ctx *cli.Context) error {
var s = gitea.StateOpen
return editIssueState(ctx, gitea.EditIssueOption{State: &s})
},
Flags: flags.AllDefaultFlags,
}

View File

@ -1,40 +1,265 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
"code.gitea.io/tea/cmd/labels"
"github.com/urfave/cli/v2"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli"
)
// CmdLabels represents to operate repositories' labels.
var CmdLabels = cli.Command{
Name: "labels",
Aliases: []string{"label"},
Category: catEntities,
Usage: "Manage issue labels",
Description: `Manage issue labels`,
ArgsUsage: " ", // command does not accept arguments
Action: runLabels,
Subcommands: []*cli.Command{
&labels.CmdLabelsList,
&labels.CmdLabelCreate,
&labels.CmdLabelUpdate,
&labels.CmdLabelDelete,
Subcommands: []cli.Command{
CmdLabelCreate,
CmdLabelUpdate,
CmdLabelDelete,
},
Flags: labels.CmdLabelsList.Flags,
Flags: append([]cli.Flag{
cli.StringFlag{
Name: "save, s",
Usage: "Save all the labels as a file",
},
}, AllDefaultFlags...),
}
func runLabels(ctx *cli.Context) error {
if ctx.Args().Len() == 1 {
return runLabelsDetails(ctx)
login, owner, repo := initCommand()
headers := []string{
"Index",
"Color",
"Name",
"Description",
}
return labels.RunLabelsList(ctx)
var values [][]string
labels, err := login.Client().ListRepoLabels(owner, repo)
if err != nil {
log.Fatal(err)
}
if len(labels) == 0 {
Output(outputValue, headers, values)
return nil
}
fPath := ctx.String("save")
if len(fPath) > 0 {
f, err := os.Create(fPath)
if err != nil {
return err
}
defer f.Close()
for _, label := range labels {
fmt.Fprintf(f, "#%s %s\n", label.Color, label.Name)
}
} else {
for _, label := range labels {
values = append(
values,
[]string{
strconv.FormatInt(label.ID, 10),
label.Color,
label.Name,
label.Description,
},
)
}
Output(outputValue, headers, values)
}
return nil
}
func runLabelsDetails(ctx *cli.Context) error {
return fmt.Errorf("Not yet implemented")
// CmdLabelCreate represents a sub command of labels to create label.
var CmdLabelCreate = cli.Command{
Name: "create",
Usage: "Create a label",
Description: `Create a label`,
Action: runLabelCreate,
Flags: []cli.Flag{
cli.StringFlag{
Name: "name",
Usage: "label name",
},
cli.StringFlag{
Name: "color",
Usage: "label color value",
},
cli.StringFlag{
Name: "description",
Usage: "label description",
},
cli.StringFlag{
Name: "file",
Usage: "indicate a label file",
},
},
}
func splitLabelLine(line string) (string, string, string) {
fields := strings.SplitN(line, ";", 2)
var color, name, description string
if len(fields) < 1 {
return "", "", ""
} else if len(fields) >= 2 {
description = strings.TrimSpace(fields[1])
}
fields = strings.Fields(fields[0])
if len(fields) <= 0 {
return "", "", ""
}
color = fields[0]
if len(fields) == 2 {
name = fields[1]
} else if len(fields) > 2 {
name = strings.Join(fields[1:], " ")
}
return color, name, description
}
func runLabelCreate(ctx *cli.Context) error {
login, owner, repo := initCommand()
labelFile := ctx.String("file")
var err error
if len(labelFile) == 0 {
_, err = login.Client().CreateLabel(owner, repo, gitea.CreateLabelOption{
Name: ctx.String("name"),
Color: ctx.String("color"),
Description: ctx.String("description"),
})
} else {
f, err := os.Open(labelFile)
if err != nil {
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
var i = 1
// FIXME: if Gitea's API support create multiple labels once, we should move to that API.
for scanner.Scan() {
line := scanner.Text()
color, name, description := splitLabelLine(line)
if color == "" || name == "" {
log.Printf("Line %d ignored because lack of enough fields: %s\n", i, line)
} else {
_, err = login.Client().CreateLabel(owner, repo, gitea.CreateLabelOption{
Name: name,
Color: color,
Description: description,
})
}
i++
}
}
if err != nil {
log.Fatal(err)
}
return nil
}
// CmdLabelUpdate represents a sub command of labels to update label.
var CmdLabelUpdate = cli.Command{
Name: "update",
Usage: "Update a label",
Description: `Update a label`,
Action: runLabelUpdate,
Flags: []cli.Flag{
cli.IntFlag{
Name: "id",
Usage: "label id",
},
cli.StringFlag{
Name: "name",
Usage: "label name",
},
cli.StringFlag{
Name: "color",
Usage: "label color value",
},
cli.StringFlag{
Name: "description",
Usage: "label description",
},
},
}
func runLabelUpdate(ctx *cli.Context) error {
login, owner, repo := initCommand()
id := ctx.Int64("id")
var pName, pColor, pDescription *string
name := ctx.String("name")
if name != "" {
pName = &name
}
color := ctx.String("color")
if color != "" {
pColor = &color
}
description := ctx.String("description")
if description != "" {
pDescription = &description
}
var err error
_, err = login.Client().EditLabel(owner, repo, id, gitea.EditLabelOption{
Name: pName,
Color: pColor,
Description: pDescription,
})
if err != nil {
log.Fatal(err)
}
return nil
}
// CmdLabelDelete represents a sub command of labels to delete label.
var CmdLabelDelete = cli.Command{
Name: "delete",
Usage: "Delete a label",
Description: `Delete a label`,
Action: runLabelCreate,
Flags: []cli.Flag{
cli.IntFlag{
Name: "id",
Usage: "label id",
},
},
}
func runLabelDelete(ctx *cli.Context) error {
login, owner, repo := initCommand()
err := login.Client().DeleteLabel(owner, repo, ctx.Int64("id"))
if err != nil {
log.Fatal(err)
}
return nil
}

View File

@ -1,107 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package labels
import (
"bufio"
"log"
"os"
"strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdLabelCreate represents a sub command of labels to create label.
var CmdLabelCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create a label",
Description: `Create a label`,
ArgsUsage: " ", // command does not accept arguments
Action: runLabelCreate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "label name",
},
&cli.StringFlag{
Name: "color",
Usage: "label color value",
},
&cli.StringFlag{
Name: "description",
Usage: "label description",
},
&cli.StringFlag{
Name: "file",
Usage: "indicate a label file",
},
}, flags.AllDefaultFlags...),
}
func runLabelCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
labelFile := ctx.String("file")
var err error
if len(labelFile) == 0 {
_, _, err = ctx.Login.Client().CreateLabel(ctx.Owner, ctx.Repo, gitea.CreateLabelOption{
Name: ctx.String("name"),
Color: ctx.String("color"),
Description: ctx.String("description"),
})
} else {
f, err := os.Open(labelFile)
if err != nil {
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
var i = 1
for scanner.Scan() {
line := scanner.Text()
color, name, description := splitLabelLine(line)
if color == "" || name == "" {
log.Printf("Line %d ignored because lack of enough fields: %s\n", i, line)
} else {
_, _, err = ctx.Login.Client().CreateLabel(ctx.Owner, ctx.Repo, gitea.CreateLabelOption{
Name: name,
Color: color,
Description: description,
})
}
i++
}
}
return err
}
func splitLabelLine(line string) (string, string, string) {
fields := strings.SplitN(line, ";", 2)
var color, name, description string
if len(fields) < 1 {
return "", "", ""
} else if len(fields) >= 2 {
description = strings.TrimSpace(fields[1])
}
fields = strings.Fields(fields[0])
if len(fields) <= 0 {
return "", "", ""
}
color = fields[0]
if len(fields) == 2 {
name = fields[1]
} else if len(fields) > 2 {
name = strings.Join(fields[1:], " ")
}
return color, name, description
}

View File

@ -1,35 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package labels
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v2"
)
// CmdLabelDelete represents a sub command of labels to delete label.
var CmdLabelDelete = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "Delete a label",
Description: `Delete a label`,
ArgsUsage: " ", // command does not accept arguments
Action: runLabelDelete,
Flags: append([]cli.Flag{
&cli.IntFlag{
Name: "id",
Usage: "label id",
},
}, flags.AllDefaultFlags...),
}
func runLabelDelete(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
_, err := ctx.Login.Client().DeleteLabel(ctx.Owner, ctx.Repo, ctx.Int64("id"))
return err
}

View File

@ -1,54 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package labels
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/task"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdLabelsList represents a sub command of labels to list labels
var CmdLabelsList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List labels",
Description: "List labels",
ArgsUsage: " ", // command does not accept arguments
Action: RunLabelsList,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "save",
Aliases: []string{"s"},
Usage: "Save all the labels as a file",
},
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
// RunLabelsList list labels.
func RunLabelsList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
labels, _, err := client.ListRepoLabels(ctx.Owner, ctx.Repo, gitea.ListLabelsOptions{
ListOptions: ctx.GetListOptions(),
})
if err != nil {
return err
}
if ctx.IsSet("save") {
return task.LabelsExport(labels, ctx.String("save"))
}
print.LabelsList(labels, ctx.Output)
return nil
}

View File

@ -1,74 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package labels
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdLabelUpdate represents a sub command of labels to update label.
var CmdLabelUpdate = cli.Command{
Name: "update",
Usage: "Update a label",
Description: `Update a label`,
ArgsUsage: " ", // command does not accept arguments
Action: runLabelUpdate,
Flags: append([]cli.Flag{
&cli.IntFlag{
Name: "id",
Usage: "label id",
},
&cli.StringFlag{
Name: "name",
Usage: "label name",
},
&cli.StringFlag{
Name: "color",
Usage: "label color value",
},
&cli.StringFlag{
Name: "description",
Usage: "label description",
},
}, flags.AllDefaultFlags...),
}
func runLabelUpdate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
id := ctx.Int64("id")
var pName, pColor, pDescription *string
name := ctx.String("name")
if name != "" {
pName = &name
}
color := ctx.String("color")
if color != "" {
pColor = &color
}
description := ctx.String("description")
if description != "" {
pDescription = &description
}
var err error
_, _, err = ctx.Login.Client().EditLabel(ctx.Owner, ctx.Repo, id, gitea.EditLabelOption{
Name: pName,
Color: pColor,
Description: pDescription,
})
if err != nil {
return err
}
return nil
}

View File

@ -1,7 +1,8 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package labels
package cmd
import (
"bufio"

111
cmd/log.go Normal file
View File

@ -0,0 +1,111 @@
// Copyright 2018 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"
"os"
"strconv"
"strings"
"github.com/olekukonko/tablewriter"
)
var (
showLog bool
)
// Println println content according the flag
func Println(a ...interface{}) {
if showLog {
fmt.Println(a...)
}
}
// Printf printf content according the flag
func Printf(format string, a ...interface{}) {
if showLog {
fmt.Printf(format, a...)
}
}
// Error println content as an error information
func Error(a ...interface{}) {
fmt.Println(a...)
}
// Errorf printf content as an error information
func Errorf(format string, a ...interface{}) {
fmt.Printf(format, a...)
}
// outputtable prints structured data as table
func outputtable(headers []string, values [][]string) {
table := tablewriter.NewWriter(os.Stdout)
if len(headers) > 0 {
table.SetHeader(headers)
}
for _, value := range values {
table.Append(value)
}
table.Render()
}
// outputsimple prints structured data as space delimited value
func outputsimple(headers []string, values [][]string) {
for _, value := range values {
fmt.Printf(strings.Join(value, " "))
fmt.Printf("\n")
}
}
// outputdsv prints structured data as delimiter separated value format
func outputdsv(headers []string, values [][]string, delimiterOpt ...string) {
delimiter := ","
if len(delimiterOpt) > 0 {
delimiter = delimiterOpt[0]
}
fmt.Println("\"" + strings.Join(headers, "\""+delimiter+"\"") + "\"")
for _, value := range values {
fmt.Printf("\"")
fmt.Printf(strings.Join(value, "\""+delimiter+"\""))
fmt.Printf("\"")
fmt.Printf("\n")
}
}
// outputyaml prints structured data as yaml
func outputyaml(headers []string, values [][]string) {
for _, value := range values {
fmt.Println("-")
for j, val := range value {
intVal, _ := strconv.Atoi(val)
if strconv.Itoa(intVal) == val {
fmt.Printf(" %s: %s\n", headers[j], val)
} else {
fmt.Printf(" %s: '%s'\n", headers[j], val)
}
}
}
}
// Output provides general function to convert given information
// into several outputs
func Output(output string, headers []string, values [][]string) {
switch {
case output == "" || output == "table":
outputtable(headers, values)
case output == "csv":
outputdsv(headers, values, ",")
case output == "simple":
outputsimple(headers, values)
case output == "tsv":
outputdsv(headers, values, "\t")
case output == "yaml":
outputyaml(headers, values)
default:
Errorf("unknown output type '" + output + "', available types are:\n- csv: comma-separated values\n- simple: space-separated values\n- table: auto-aligned table format (default)\n- tsv: tab-separated values\n- yaml: YAML format\n")
}
}

View File

@ -1,51 +1,132 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Copyright 2018 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 (
"crypto/tls"
"fmt"
"log"
"net/http"
"net/http/cookiejar"
"code.gitea.io/tea/cmd/login"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
"github.com/urfave/cli"
)
// CmdLogin represents to login a gitea server.
var CmdLogin = cli.Command{
Name: "logins",
Aliases: []string{"login"},
Category: catSetup,
Name: "login",
Usage: "Log in to a Gitea server",
Description: `Log in to a Gitea server`,
ArgsUsage: "[<login name>]",
Action: runLogins,
Subcommands: []*cli.Command{
&login.CmdLoginList,
&login.CmdLoginAdd,
&login.CmdLoginEdit,
&login.CmdLoginDelete,
&login.CmdLoginSetDefault,
&login.CmdLoginHelper,
Subcommands: []cli.Command{
cmdLoginList,
cmdLoginAdd,
},
}
func runLogins(ctx *cli.Context) error {
if ctx.Args().Len() == 1 {
return runLoginDetail(ctx.Args().First())
}
return login.RunLoginList(ctx)
// CmdLogin represents to login a gitea server.
var cmdLoginAdd = cli.Command{
Name: "add",
Usage: "Add a Gitea login",
Description: `Add a Gitea login`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "name, n",
Usage: "Login name",
},
cli.StringFlag{
Name: "url, u",
Value: "https://try.gitea.io",
EnvVar: "GITEA_SERVER_URL",
Usage: "Server URL",
},
cli.StringFlag{
Name: "token, t",
Value: "",
EnvVar: "GITEA_SERVER_TOKEN",
Usage: "Access token. Can be obtained from Settings > Applications",
},
cli.BoolFlag{
Name: "insecure, i",
Usage: "Disable TLS verification",
},
},
Action: runLoginAdd,
}
func runLoginDetail(name string) error {
l := config.GetLoginByName(name)
if l == nil {
fmt.Printf("Login '%s' do not exist\n\n", name)
return nil
func runLoginAdd(ctx *cli.Context) error {
if !ctx.IsSet("url") {
log.Fatal("You have to input Gitea server URL")
}
if !ctx.IsSet("token") {
log.Fatal("No token found")
}
if !ctx.IsSet("name") {
log.Fatal("You have to set a name for the login")
}
err := loadConfig(yamlConfigPath)
if err != nil {
log.Fatal("Unable to load config file " + yamlConfigPath)
}
client := gitea.NewClient(ctx.String("url"), ctx.String("token"))
if ctx.Bool("insecure") {
cookieJar, _ := cookiejar.New(nil)
client.SetHTTPClient(&http.Client{
Jar: cookieJar,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
})
}
u, err := client.GetMyUserInfo()
if err != nil {
log.Fatal(err)
}
fmt.Println("Login successful! Login name " + u.UserName)
err = addLogin(Login{
Name: ctx.String("name"),
URL: ctx.String("url"),
Token: ctx.String("token"),
Insecure: ctx.Bool("insecure"),
})
if err != nil {
log.Fatal(err)
}
err = saveConfig(yamlConfigPath)
if err != nil {
log.Fatal(err)
}
return nil
}
// CmdLogin represents to login a gitea server.
var cmdLoginList = cli.Command{
Name: "ls",
Usage: "List Gitea logins",
Description: `List Gitea logins`,
Action: runLoginList,
}
func runLoginList(ctx *cli.Context) error {
err := loadConfig(yamlConfigPath)
if err != nil {
log.Fatal("Unable to load config file " + yamlConfigPath)
}
fmt.Printf("Name\tURL\tSSHHost\n")
for _, l := range config.Logins {
fmt.Printf("%s\t%s\t%s\n", l.Name, l.URL, l.GetSSHHost())
}
print.LoginDetails(l)
return nil
}

View File

@ -1,124 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task"
"github.com/urfave/cli/v2"
)
// CmdLoginAdd represents to login a gitea server.
var CmdLoginAdd = cli.Command{
Name: "add",
Usage: "Add a Gitea login",
Description: `Add a Gitea login, without args it will create one interactively`,
ArgsUsage: " ", // command does not accept arguments
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: "Login name",
},
&cli.StringFlag{
Name: "url",
Aliases: []string{"u"},
Value: "https://gitea.com",
EnvVars: []string{"GITEA_SERVER_URL"},
Usage: "Server URL",
},
&cli.BoolFlag{
Name: "no-version-check",
Aliases: []string{"nv"},
Usage: "Do not check version of Gitea instance",
},
&cli.StringFlag{
Name: "token",
Aliases: []string{"t"},
Value: "",
EnvVars: []string{"GITEA_SERVER_TOKEN"},
Usage: "Access token. Can be obtained from Settings > Applications",
},
&cli.StringFlag{
Name: "user",
Value: "",
EnvVars: []string{"GITEA_SERVER_USER"},
Usage: "User for basic auth (will create token)",
},
&cli.StringFlag{
Name: "password",
Aliases: []string{"pwd"},
Value: "",
EnvVars: []string{"GITEA_SERVER_PASSWORD"},
Usage: "Password for basic auth (will create token)",
},
&cli.StringFlag{
Name: "otp",
EnvVars: []string{"GITEA_SERVER_OTP"},
Usage: "OTP token for auth, if necessary",
},
&cli.StringFlag{
Name: "scopes",
EnvVars: []string{"GITEA_SCOPES"},
Usage: "Token scopes to add when creating a new token, separated by a comma",
},
&cli.StringFlag{
Name: "ssh-key",
Aliases: []string{"s"},
Usage: "Path to a SSH key/certificate to use, overrides auto-discovery",
},
&cli.BoolFlag{
Name: "insecure",
Aliases: []string{"i"},
Usage: "Disable TLS verification",
},
&cli.StringFlag{
Name: "ssh-agent-principal",
Aliases: []string{"c"},
Usage: "Use SSH certificate with specified principal to login (needs a running ssh-agent with certificate loaded)",
},
&cli.StringFlag{
Name: "ssh-agent-key",
Aliases: []string{"a"},
Usage: "Use SSH public key or SSH fingerprint to login (needs a running ssh-agent with ssh key loaded)",
},
&cli.BoolFlag{
Name: "helper",
Aliases: []string{"j"},
Usage: "Add helper",
},
},
Action: runLoginAdd,
}
func runLoginAdd(ctx *cli.Context) error {
// if no args create login interactive
if ctx.NumFlags() == 0 {
return interact.CreateLogin()
}
sshAgent := false
if ctx.String("ssh-agent-key") != "" || ctx.String("ssh-agent-principal") != "" {
sshAgent = true
}
// else use args to add login
return task.CreateLogin(
ctx.String("name"),
ctx.String("token"),
ctx.String("user"),
ctx.String("password"),
ctx.String("otp"),
ctx.String("scopes"),
ctx.String("ssh-key"),
ctx.String("url"),
ctx.String("ssh-agent-principal"),
ctx.String("ssh-agent-key"),
ctx.Bool("insecure"),
sshAgent,
!ctx.Bool("no-version-check"),
ctx.Bool("helper"),
)
}

View File

@ -1,37 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"github.com/urfave/cli/v2"
)
// CmdLoginSetDefault represents to login a gitea server.
var CmdLoginSetDefault = cli.Command{
Name: "default",
Usage: "Get or Set Default Login",
Description: `Get or Set Default Login`,
ArgsUsage: "<Login>",
Action: runLoginSetDefault,
Flags: []cli.Flag{&flags.OutputFlag},
}
func runLoginSetDefault(ctx *cli.Context) error {
if ctx.Args().Len() == 0 {
l, err := config.GetDefaultLogin()
if err != nil {
return err
}
fmt.Printf("Default Login: %s\n", l.Name)
return nil
}
name := ctx.Args().First()
return config.SetDefaultLogin(name)
}

View File

@ -1,43 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"errors"
"log"
"code.gitea.io/tea/modules/config"
"github.com/urfave/cli/v2"
)
// CmdLoginDelete is a command to delete a login
var CmdLoginDelete = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "Remove a Gitea login",
Description: `Remove a Gitea login`,
ArgsUsage: "<login name>",
Action: RunLoginDelete,
}
// RunLoginDelete runs the action of a login delete command
func RunLoginDelete(ctx *cli.Context) error {
logins, err := config.GetLogins()
if err != nil {
log.Fatal(err)
}
var name string
if len(ctx.Args().First()) != 0 {
name = ctx.Args().First()
} else if len(logins) == 1 {
name = logins[0].Name
} else {
return errors.New("Please specify a login name")
}
return config.DeleteLogin(name)
}

View File

@ -1,27 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"github.com/skratchdot/open-golang/open"
"github.com/urfave/cli/v2"
)
// CmdLoginEdit represents to login a gitea server.
var CmdLoginEdit = cli.Command{
Name: "edit",
Aliases: []string{"e"},
Usage: "Edit Gitea logins",
Description: `Edit Gitea logins`,
ArgsUsage: " ", // command does not accept arguments
Action: runLoginEdit,
Flags: []cli.Flag{&flags.OutputFlag},
}
func runLoginEdit(_ *cli.Context) error {
return open.Start(config.GetConfigPath())
}

View File

@ -1,112 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"bufio"
"fmt"
"log"
"net/url"
"os"
"strings"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/task"
"github.com/urfave/cli/v2"
)
// CmdLoginHelper represents to login a gitea helper.
var CmdLoginHelper = cli.Command{
Name: "helper",
Aliases: []string{"git-credential"},
Usage: "Git helper",
Description: `Git helper`,
Hidden: true,
Subcommands: []*cli.Command{
{
Name: "store",
Description: "Command drops",
Aliases: []string{"erase"},
Action: func(ctx *cli.Context) error {
return nil
},
},
{
Name: "setup",
Description: "Setup helper to tea authenticate",
Action: func(ctx *cli.Context) error {
logins, err := config.GetLogins()
if err != nil {
return err
}
for _, login := range logins {
added, err := task.SetupHelper(login)
if err != nil {
return err
} else if added {
fmt.Printf("Added \"%s\"\n", login.Name)
} else {
fmt.Printf("\"%s\" has already been added!\n", login.Name)
}
}
return nil
},
},
{
Name: "get",
Description: "Get token to auth",
Action: func(cmd *cli.Context) error {
wants := map[string]string{}
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
line := s.Text()
if line == "" {
break
}
parts := strings.SplitN(line, "=", 2)
if len(parts) < 2 {
continue
}
key, value := parts[0], parts[1]
if key == "url" {
u, err := url.Parse(value)
if err != nil {
return err
}
wants["protocol"] = u.Scheme
wants["host"] = u.Host
wants["path"] = u.Path
wants["username"] = u.User.Username()
wants["password"], _ = u.User.Password()
} else {
wants[key] = value
}
}
if len(wants["host"]) == 0 {
log.Fatal("Require hostname")
} else if len(wants["protocol"]) == 0 {
wants["protocol"] = "http"
}
userConfig := config.GetLoginByHost(wants["host"])
if len(userConfig.Token) == 0 {
log.Fatal("User no set")
}
host, err := url.Parse(userConfig.URL)
if err != nil {
return err
}
_, err = fmt.Fprintf(os.Stdout, "protocol=%s\nhost=%s\nusername=%s\npassword=%s\n", host.Scheme, host.Host, userConfig.User, userConfig.Token)
if err != nil {
return err
}
return nil
},
},
},
}

View File

@ -1,33 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v2"
)
// CmdLoginList represents to login a gitea server.
var CmdLoginList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List Gitea logins",
Description: `List Gitea logins`,
ArgsUsage: " ", // command does not accept arguments
Action: RunLoginList,
Flags: []cli.Flag{&flags.OutputFlag},
}
// RunLoginList list all logins
func RunLoginList(cmd *cli.Context) error {
logins, err := config.GetLogins()
if err != nil {
return err
}
print.LoginsList(logins, cmd.String("output"))
return nil
}

View File

@ -1,20 +1,60 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Copyright 2018 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/login"
"errors"
"log"
"os"
"github.com/urfave/cli/v2"
"github.com/urfave/cli"
)
// CmdLogout represents to logout a gitea server.
var CmdLogout = cli.Command{
Name: "logout",
Category: catSetup,
Usage: "Log out from a Gitea server",
Description: `Log out from a Gitea server`,
ArgsUsage: "<login name>",
Action: login.RunLoginDelete,
Action: runLogout,
Flags: []cli.Flag{
cli.StringFlag{
Name: "name, n",
Usage: "Login name to remove",
},
},
}
func runLogout(ctx *cli.Context) error {
var name string
if len(os.Args) == 3 {
name = os.Args[2]
} else if ctx.IsSet("name") {
name = ctx.String("name")
} else {
return errors.New("Please specify a login name")
}
err := loadConfig(yamlConfigPath)
if err != nil {
log.Fatal("Unable to load config file " + yamlConfigPath)
}
var idx = -1
for i, l := range config.Logins {
if l.Name == name {
idx = i
break
}
}
if idx > -1 {
config.Logins = append(config.Logins[:idx], config.Logins[idx+1:]...)
err = saveConfig(yamlConfigPath)
if err != nil {
log.Fatal("Unable to save config file " + yamlConfigPath)
}
}
return nil
}

View File

@ -1,53 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"code.gitea.io/tea/cmd/milestones"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v2"
)
// CmdMilestones represents to operate repositories milestones.
var CmdMilestones = cli.Command{
Name: "milestones",
Aliases: []string{"milestone", "ms"},
Category: catEntities,
Usage: "List and create milestones",
Description: `List and create milestones`,
ArgsUsage: "[<milestone name>]",
Action: runMilestones,
Subcommands: []*cli.Command{
&milestones.CmdMilestonesList,
&milestones.CmdMilestonesCreate,
&milestones.CmdMilestonesClose,
&milestones.CmdMilestonesDelete,
&milestones.CmdMilestonesReopen,
&milestones.CmdMilestonesIssues,
},
Flags: milestones.CmdMilestonesList.Flags,
}
func runMilestones(ctx *cli.Context) error {
if ctx.Args().Len() == 1 {
return runMilestoneDetail(ctx, ctx.Args().First())
}
return milestones.RunMilestonesList(ctx)
}
func runMilestoneDetail(cmd *cli.Context, name string) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
milestone, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, name)
if err != nil {
return err
}
print.MilestoneDetails(milestone)
return nil
}

View File

@ -1,31 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package milestones
import (
"code.gitea.io/tea/cmd/flags"
"github.com/urfave/cli/v2"
)
// CmdMilestonesClose represents a sub command of milestones to close an milestone
var CmdMilestonesClose = cli.Command{
Name: "close",
Usage: "Change state of one or more milestones to 'closed'",
Description: `Change state of one or more milestones to 'closed'`,
ArgsUsage: "<milestone name> [<milestone name>...]",
Action: func(ctx *cli.Context) error {
if ctx.Bool("force") {
return deleteMilestone(ctx)
}
return editMilestoneStatus(ctx, true)
},
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: "delete milestone",
},
}, flags.AllDefaultFlags...),
}

View File

@ -1,82 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package milestones
import (
"time"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task"
"code.gitea.io/sdk/gitea"
"github.com/araddon/dateparse"
"github.com/urfave/cli/v2"
)
// CmdMilestonesCreate represents a sub command of milestones to create milestone
var CmdMilestonesCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create an milestone on repository",
Description: `Create an milestone on repository`,
ArgsUsage: " ", // command does not accept arguments
Action: runMilestonesCreate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
Usage: "milestone title to create",
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"d"},
Usage: "milestone description to create",
},
&cli.StringFlag{
Name: "deadline",
Aliases: []string{"expires", "x"},
Usage: "set milestone deadline (default is no due date)",
},
&cli.StringFlag{
Name: "state",
Usage: "set milestone state (default is open)",
DefaultText: "open",
},
}, flags.AllDefaultFlags...),
}
func runMilestonesCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
date := ctx.String("deadline")
deadline := &time.Time{}
if date != "" {
t, err := dateparse.ParseAny(date)
if err != nil {
return err
}
deadline = &t
}
state := gitea.StateOpen
if ctx.String("state") == "closed" {
state = gitea.StateClosed
}
if ctx.NumFlags() == 0 {
return interact.CreateMilestone(ctx.Login, ctx.Owner, ctx.Repo)
}
return task.CreateMilestone(
ctx.Login,
ctx.Owner,
ctx.Repo,
ctx.String("title"),
ctx.String("description"),
deadline,
state,
)
}

View File

@ -1,31 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package milestones
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v2"
)
// CmdMilestonesDelete represents a sub command of milestones to delete an milestone
var CmdMilestonesDelete = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "delete a milestone",
Description: "delete a milestone",
ArgsUsage: "<milestone name>",
Action: deleteMilestone,
Flags: flags.AllDefaultFlags,
}
func deleteMilestone(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
_, err := client.DeleteMilestoneByName(ctx.Owner, ctx.Repo, ctx.Args().First())
return err
}

View File

@ -1,182 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package milestones
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"
)
var msIssuesFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{
"index", "kind", "title", "state", "updated", "labels",
})
// CmdMilestonesIssues represents a sub command of milestones to manage issue/pull of an milestone
var CmdMilestonesIssues = cli.Command{
Name: "issues",
Aliases: []string{"i"},
Usage: "manage issue/pull of an milestone",
Description: "manage issue/pull of an milestone",
ArgsUsage: "<milestone name>",
Action: runMilestoneIssueList,
Subcommands: []*cli.Command{
&CmdMilestoneAddIssue,
&CmdMilestoneRemoveIssue,
},
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "state",
Usage: "Filter by issue state (all|open|closed)",
DefaultText: "open",
},
&cli.StringFlag{
Name: "kind",
Usage: "Filter by kind (issue|pull)",
},
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
msIssuesFieldsFlag,
}, flags.AllDefaultFlags...),
}
// CmdMilestoneAddIssue represents a sub command of milestone issues to add an issue/pull to an milestone
var CmdMilestoneAddIssue = cli.Command{
Name: "add",
Aliases: []string{"a"},
Usage: "Add an issue/pull to an milestone",
Description: "Add an issue/pull to an milestone",
ArgsUsage: "<milestone name> <issue/pull index>",
Action: runMilestoneIssueAdd,
Flags: flags.AllDefaultFlags,
}
// CmdMilestoneRemoveIssue represents a sub command of milestones to remove an issue/pull from an milestone
var CmdMilestoneRemoveIssue = cli.Command{
Name: "remove",
Aliases: []string{"r"},
Usage: "Remove an issue/pull to an milestone",
Description: "Remove an issue/pull to an milestone",
ArgsUsage: "<milestone name> <issue/pull index>",
Action: runMilestoneIssueRemove,
Flags: flags.AllDefaultFlags,
}
func runMilestoneIssueList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
state := gitea.StateOpen
switch ctx.String("state") {
case "all":
state = gitea.StateAll
case "closed":
state = gitea.StateClosed
}
kind := gitea.IssueTypeAll
switch ctx.String("kind") {
case "issue":
kind = gitea.IssueTypeIssue
case "pull":
kind = gitea.IssueTypePull
}
if ctx.Args().Len() != 1 {
return fmt.Errorf("Must specify milestone name")
}
milestone := ctx.Args().First()
// make sure milestone exist
_, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestone)
if err != nil {
return err
}
issues, _, err := client.ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{
ListOptions: ctx.GetListOptions(),
Milestones: []string{milestone},
Type: kind,
State: state,
})
if err != nil {
return err
}
fields, err := msIssuesFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
print.IssuesPullsList(issues, ctx.Output, fields)
return nil
}
func runMilestoneIssueAdd(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
if ctx.Args().Len() != 2 {
return fmt.Errorf("need two arguments")
}
mileName := ctx.Args().Get(0)
issueIndex := ctx.Args().Get(1)
idx, err := utils.ArgToIndex(issueIndex)
if err != nil {
return err
}
// make sure milestone exist
mile, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, mileName)
if err != nil {
return err
}
_, _, err = client.EditIssue(ctx.Owner, ctx.Repo, idx, gitea.EditIssueOption{
Milestone: &mile.ID,
})
return err
}
func runMilestoneIssueRemove(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
if ctx.Args().Len() != 2 {
return fmt.Errorf("need two arguments")
}
mileName := ctx.Args().Get(0)
issueIndex := ctx.Args().Get(1)
idx, err := utils.ArgToIndex(issueIndex)
if err != nil {
return err
}
issue, _, err := client.GetIssue(ctx.Owner, ctx.Repo, idx)
if err != nil {
return err
}
if issue.Milestone == nil {
return fmt.Errorf("issue is not assigned to a milestone")
}
if issue.Milestone.Title != mileName {
return fmt.Errorf("issue is not assigned to this milestone")
}
zero := int64(0)
_, _, err = client.EditIssue(ctx.Owner, ctx.Repo, idx, gitea.EditIssueOption{
Milestone: &zero,
})
return err
}

View File

@ -1,72 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package milestones
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 fieldsFlag = flags.FieldsFlag(print.MilestoneFields, []string{
"title", "items", "duedate",
})
// CmdMilestonesList represents a sub command of milestones to list milestones
var CmdMilestonesList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List milestones of the repository",
Description: `List milestones of the repository`,
ArgsUsage: " ", // command does not accept arguments
Action: RunMilestonesList,
Flags: append([]cli.Flag{
fieldsFlag,
&cli.StringFlag{
Name: "state",
Usage: "Filter by milestone state (all|open|closed)",
DefaultText: "open",
},
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
// RunMilestonesList list milestones
func RunMilestonesList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
fields, err := fieldsFlag.GetValues(cmd)
if err != nil {
return err
}
state := gitea.StateOpen
switch ctx.String("state") {
case "all":
state = gitea.StateAll
if !cmd.IsSet("fields") { // add to default fields
fields = append(fields, "state")
}
case "closed":
state = gitea.StateClosed
}
client := ctx.Login.Client()
milestones, _, err := client.ListRepoMilestones(ctx.Owner, ctx.Repo, gitea.ListMilestoneOption{
ListOptions: ctx.GetListOptions(),
State: state,
})
if err != nil {
return err
}
print.MilestonesList(milestones, ctx.Output, fields)
return nil
}

View File

@ -1,60 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package milestones
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"
)
// CmdMilestonesReopen represents a sub command of milestones to open an milestone
var CmdMilestonesReopen = cli.Command{
Name: "reopen",
Aliases: []string{"open"},
Usage: "Change state of one or more milestones to 'open'",
Description: `Change state of one or more milestones to 'open'`,
ArgsUsage: "<milestone name> [<milestone name> ...]",
Action: func(ctx *cli.Context) error {
return editMilestoneStatus(ctx, false)
},
Flags: flags.AllDefaultFlags,
}
func editMilestoneStatus(cmd *cli.Context, close bool) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() == 0 {
return fmt.Errorf(ctx.Command.ArgsUsage)
}
state := gitea.StateOpen
if close {
state = gitea.StateClosed
}
client := ctx.Login.Client()
for _, ms := range ctx.Args().Slice() {
opts := gitea.EditMilestoneOption{
State: &state,
Title: ms,
}
milestone, _, err := client.EditMilestoneByName(ctx.Owner, ctx.Repo, ms, opts)
if err != nil {
return err
}
if ctx.Args().Len() > 1 {
fmt.Printf("%s/milestone/%d\n", ctx.GetRemoteRepoHTMLURL(), milestone.ID)
} else {
print.MilestoneDetails(milestone)
}
}
return nil
}

View File

@ -1,28 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"code.gitea.io/tea/cmd/notifications"
"github.com/urfave/cli/v2"
)
// CmdNotifications is the main command to operate with notifications
var CmdNotifications = cli.Command{
Name: "notifications",
Aliases: []string{"notification", "n"},
Category: catHelpers,
Usage: "Show notifications",
Description: "Show notifications, by default based on the current repo if available",
Action: notifications.RunNotificationsList,
Subcommands: []*cli.Command{
&notifications.CmdNotificationsList,
&notifications.CmdNotificationsMarkRead,
&notifications.CmdNotificationsMarkUnread,
&notifications.CmdNotificationsMarkPinned,
&notifications.CmdNotificationsUnpin,
},
Flags: notifications.CmdNotificationsList.Flags,
}

View File

@ -1,106 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package notifications
import (
"log"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
var notifyFieldsFlag = flags.FieldsFlag(print.NotificationFields, []string{
"id", "status", "index", "type", "state", "title",
})
var notifyTypeFlag = flags.NewCsvFlag("types", "subject types to filter by", []string{"t"},
[]string{"issue", "pull", "repository", "commit"}, nil)
// CmdNotificationsList represents a sub command of notifications to list notifications
var CmdNotificationsList = cli.Command{
Name: "ls",
Aliases: []string{"list"},
Usage: "List notifications",
Description: `List notifications`,
ArgsUsage: " ", // command does not accept arguments
Action: RunNotificationsList,
Flags: append([]cli.Flag{
notifyFieldsFlag,
notifyTypeFlag,
}, flags.NotificationFlags...),
}
// RunNotificationsList list notifications
func RunNotificationsList(ctx *cli.Context) error {
var states []gitea.NotifyStatus
statesStr, err := flags.NotificationStateFlag.GetValues(ctx)
if err != nil {
return err
}
for _, s := range statesStr {
states = append(states, gitea.NotifyStatus(s))
}
var types []gitea.NotifySubjectType
typesStr, err := notifyTypeFlag.GetValues(ctx)
if err != nil {
return err
}
for _, t := range typesStr {
types = append(types, gitea.NotifySubjectType(t))
}
return listNotifications(ctx, states, types)
}
// listNotifications will get the notifications based on status and subject type
func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects []gitea.NotifySubjectType) error {
var news []*gitea.NotificationThread
var err error
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
all := ctx.Bool("mine")
// This enforces pagination (see https://github.com/go-gitea/gitea/issues/16733)
listOpts := ctx.GetListOptions()
if listOpts.Page == 0 {
listOpts.Page = 1
}
fields, err := notifyFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
if all {
// add repository to the default fields
if !cmd.IsSet("fields") {
fields = append(fields, "repository")
}
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
ListOptions: listOpts,
Status: status,
SubjectTypes: subjects,
})
} else {
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
news, _, err = client.ListRepoNotifications(ctx.Owner, ctx.Repo, gitea.ListNotificationOptions{
ListOptions: listOpts,
Status: status,
SubjectTypes: subjects,
})
}
if err != nil {
log.Fatal(err)
}
print.NotificationsList(news, ctx.Output, fields)
return nil
}

View File

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

View File

@ -1,77 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"path"
"strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
local_git "code.gitea.io/tea/modules/git"
"github.com/skratchdot/open-golang/open"
"github.com/urfave/cli/v2"
)
// CmdOpen represents a sub command of issues to open issue on the web browser
var CmdOpen = cli.Command{
Name: "open",
Aliases: []string{"o"},
Category: catHelpers,
Usage: "Open something of the repository in web browser",
Description: `Open something of the repository in web browser`,
Action: runOpen,
Flags: append([]cli.Flag{}, flags.LoginRepoFlags...),
}
func runOpen(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
var suffix string
number := ctx.Args().Get(0)
switch {
case strings.EqualFold(number, "issues"):
suffix = "issues"
case strings.EqualFold(number, "pulls"):
suffix = "pulls"
case strings.EqualFold(number, "releases"):
suffix = "releases"
case strings.EqualFold(number, "commits"):
repo, err := local_git.RepoForWorkdir()
if err != nil {
return err
}
b, err := repo.Head()
if err != nil {
return err
}
name := b.Name()
switch {
case name.IsBranch():
suffix = "commits/branch/" + name.Short()
case name.IsTag():
suffix = "commits/tag/" + name.Short()
}
case strings.EqualFold(number, "branches"):
suffix = "branches"
case strings.EqualFold(number, "wiki"):
suffix = "wiki"
case strings.EqualFold(number, "activity"):
suffix = "activity"
case strings.EqualFold(number, "settings"):
suffix = "settings"
case strings.EqualFold(number, "labels"):
suffix = "labels"
case strings.EqualFold(number, "milestones"):
suffix = "milestones"
case number != "":
suffix = "issues/" + number
default:
suffix = number
}
return open.Run(path.Join(ctx.GetRemoteRepoHTMLURL(), suffix))
}

View File

@ -1,47 +0,0 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"code.gitea.io/tea/cmd/organizations"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v2"
)
// CmdOrgs represents handle organization
var CmdOrgs = cli.Command{
Name: "organizations",
Aliases: []string{"organization", "org"},
Category: catEntities,
Usage: "List, create, delete organizations",
Description: "Show organization details",
ArgsUsage: "[<organization>]",
Action: runOrganizations,
Subcommands: []*cli.Command{
&organizations.CmdOrganizationList,
&organizations.CmdOrganizationCreate,
&organizations.CmdOrganizationDelete,
},
Flags: organizations.CmdOrganizationList.Flags,
}
func runOrganizations(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
if ctx.Args().Len() == 1 {
return runOrganizationDetail(ctx)
}
return organizations.RunOrganizationList(cmd)
}
func runOrganizationDetail(ctx *context.TeaContext) error {
org, _, err := ctx.Login.Client().GetOrg(ctx.Args().First())
if err != nil {
return err
}
print.OrganizationDetails(org)
return nil
}

View File

@ -1,89 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
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

@ -1,44 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organizations
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v2"
)
// CmdOrganizationDelete represents a sub command of organizations to delete a given user organization
var CmdOrganizationDelete = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "Delete users Organizations",
Description: "Delete users organizations",
ArgsUsage: "<organization name>",
Action: RunOrganizationDelete,
Flags: []cli.Flag{
&flags.LoginFlag,
&flags.RemoteFlag,
},
}
// RunOrganizationDelete delete user organization
func RunOrganizationDelete(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
if ctx.Args().Len() < 1 {
return fmt.Errorf("You have to specify the organization name you want to delete")
}
response, err := client.DeleteOrg(ctx.Args().First())
if response != nil && response.StatusCode == 404 {
return fmt.Errorf("The given organization does not exist")
}
return err
}

View File

@ -1,44 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organizations
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"
)
// CmdOrganizationList represents a sub command of organizations to list users organizations
var CmdOrganizationList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List Organizations",
Description: "List users organizations",
ArgsUsage: " ", // command does not accept arguments
Action: RunOrganizationList,
Flags: append([]cli.Flag{
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
// RunOrganizationList list user organizations
func RunOrganizationList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
userOrganizations, _, err := client.ListUserOrgs(ctx.Login.User, gitea.ListOrgsOptions{
ListOptions: ctx.GetListOptions(),
})
if err != nil {
return err
}
print.OrganizationsList(userOrganizations, ctx.Output)
return nil
}

View File

@ -1,95 +1,72 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// 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/pulls"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/tea/modules/workaround"
"log"
"strconv"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
"github.com/urfave/cli"
)
// CmdPulls is the main command to operate on PRs
// CmdPulls represents to login a gitea server.
var CmdPulls = cli.Command{
Name: "pulls",
Aliases: []string{"pull", "pr"},
Category: catEntities,
Usage: "Manage and checkout pull requests",
Description: `Lists PRs when called without argument. If PR index is provided, will show it in detail.`,
ArgsUsage: "[<pull index>]",
Usage: "List open pull requests",
Description: `List open pull requests`,
Action: runPulls,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "comments",
Usage: "Whether to display comments (will prompt if not provided & run interactively)",
},
}, pulls.CmdPullsList.Flags...),
Subcommands: []*cli.Command{
&pulls.CmdPullsList,
&pulls.CmdPullsCheckout,
&pulls.CmdPullsClean,
&pulls.CmdPullsCreate,
&pulls.CmdPullsClose,
&pulls.CmdPullsReopen,
&pulls.CmdPullsReview,
&pulls.CmdPullsApprove,
&pulls.CmdPullsReject,
&pulls.CmdPullsMerge,
},
Flags: AllDefaultFlags,
}
func runPulls(ctx *cli.Context) error {
if ctx.Args().Len() == 1 {
return runPullDetail(ctx, ctx.Args().First())
}
return pulls.RunPullsList(ctx)
}
login, owner, repo := initCommand()
func runPullDetail(cmd *cli.Context, index string) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
idx, err := utils.ArgToIndex(index)
if err != nil {
return err
}
client := ctx.Login.Client()
pr, _, err := client.GetPullRequest(ctx.Owner, ctx.Repo, idx)
if err != nil {
return err
}
if err := workaround.FixPullHeadSha(client, pr); err != nil {
return err
}
reviews, _, err := client.ListPullReviews(ctx.Owner, ctx.Repo, idx, gitea.ListPullReviewsOptions{
ListOptions: gitea.ListOptions{Page: -1},
prs, err := login.Client().ListRepoPullRequests(owner, repo, gitea.ListPullRequestsOptions{
Page: 0,
State: string(gitea.StateOpen),
})
if err != nil {
fmt.Printf("error while loading reviews: %v\n", err)
log.Fatal(err)
}
ci, _, err := client.GetCombinedStatus(ctx.Owner, ctx.Repo, pr.Head.Sha)
if err != nil {
fmt.Printf("error while loading CI: %v\n", err)
headers := []string{
"Index",
"Name",
"Updated",
"Title",
}
print.PullDetails(pr, reviews, ci)
var values [][]string
if pr.Comments > 0 {
err = interact.ShowCommentsMaybeInteractive(ctx, idx, pr.Comments)
if err != nil {
fmt.Printf("error loading comments: %v\n", err)
if len(prs) == 0 {
Output(outputValue, headers, values)
return nil
}
for _, pr := range prs {
if pr == nil {
continue
}
name := pr.Poster.FullName
if len(name) == 0 {
name = pr.Poster.UserName
}
values = append(
values,
[]string{
strconv.FormatInt(pr.Index, 10),
name,
pr.Updated.Format("2006-01-02 15:04:05"),
pr.Title,
},
)
}
Output(outputValue, headers, values)
return nil
}

View File

@ -1,44 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"fmt"
"strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdPullsApprove approves a PR
var CmdPullsApprove = cli.Command{
Name: "approve",
Aliases: []string{"lgtm", "a"},
Usage: "Approve a pull request",
Description: "Approve a pull request",
ArgsUsage: "<pull index> [<comment>]",
Action: func(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() == 0 {
return fmt.Errorf("Must specify a PR index")
}
idx, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
comment := strings.Join(ctx.Args().Tail(), " ")
return task.CreatePullReview(ctx, idx, gitea.ReviewStateApproved, comment, nil)
},
Flags: flags.AllDefaultFlags,
}

View File

@ -1,50 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CmdPullsCheckout is a command to locally checkout the given PR
var CmdPullsCheckout = cli.Command{
Name: "checkout",
Aliases: []string{"co"},
Usage: "Locally check out the given PR",
Description: `Locally check out the given PR`,
Action: runPullsCheckout,
ArgsUsage: "<pull index>",
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "branch",
Aliases: []string{"b"},
Usage: "Create a local branch if it doesn't exist yet",
},
}, flags.AllDefaultFlags...),
}
func runPullsCheckout(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{
LocalRepo: true,
RemoteRepo: true,
})
if ctx.Args().Len() != 1 {
return fmt.Errorf("Must specify a PR index")
}
idx, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
return task.PullCheckout(ctx.Login, ctx.Owner, ctx.Repo, ctx.Bool("branch"), idx, interact.PromptPassword)
}

View File

@ -1,46 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CmdPullsClean removes the remote and local feature branches, if a PR is merged.
var CmdPullsClean = cli.Command{
Name: "clean",
Usage: "Deletes local & remote feature-branches for a closed pull request",
Description: `Deletes local & remote feature-branches for a closed pull request`,
ArgsUsage: "<pull index>",
Action: runPullsClean,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "ignore-sha",
Usage: "Find the local branch by name instead of commit hash (less precise)",
},
}, flags.AllDefaultFlags...),
}
func runPullsClean(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{LocalRepo: true})
if ctx.Args().Len() != 1 {
return fmt.Errorf("Must specify a PR index")
}
idx, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
return task.PullClean(ctx.Login, ctx.Owner, ctx.Repo, idx, ctx.Bool("ignore-sha"), interact.PromptPassword)
}

View File

@ -1,24 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdPullsClose closes a given open pull request
var CmdPullsClose = cli.Command{
Name: "close",
Usage: "Change state of one or more pull requests to 'closed'",
Description: `Change state of one or more pull requests to 'closed'`,
ArgsUsage: "<pull index> [<pull index>...]",
Action: func(ctx *cli.Context) error {
var s = gitea.StateClosed
return editPullState(ctx, gitea.EditPullRequestOption{State: &s})
},
Flags: flags.AllDefaultFlags,
}

View File

@ -1,62 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task"
"github.com/urfave/cli/v2"
)
// CmdPullsCreate creates a pull request
var CmdPullsCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create a pull-request",
Description: "Create a pull-request in the current repo",
Action: runPullsCreate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "head",
Usage: "Branch name of the PR source (default is current one). To specify a different head repo, use <user>:<branch>",
},
&cli.StringFlag{
Name: "base",
Aliases: []string{"b"},
Usage: "Branch name of the PR target (default is repos default branch)",
},
&cli.BoolFlag{
Name: "allow-maintainer-edits",
Aliases: []string{"edits"},
Usage: "Enable maintainers to push to the base branch of created pull",
Value: true,
},
}, flags.IssuePRCreateFlags...),
}
func runPullsCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
// no args -> interactive mode
if ctx.NumFlags() == 0 {
return interact.CreatePull(ctx)
}
// else use args to create PR
opts, err := flags.GetIssuePRCreateFlags(ctx)
if err != nil {
return err
}
return task.CreatePull(
ctx,
ctx.String("base"),
ctx.String("head"),
ctx.Bool("allow-maintainer-edits"),
opts,
)
}

View File

@ -1,44 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"fmt"
"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"
)
// editPullState abstracts the arg parsing to edit the given pull request
func editPullState(cmd *cli.Context, opts gitea.EditPullRequestOption) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() == 0 {
return fmt.Errorf("Please provide a Pull Request index")
}
indices, err := utils.ArgsToIndices(ctx.Args().Slice())
if err != nil {
return err
}
client := ctx.Login.Client()
for _, index := range indices {
pr, _, err := client.EditPullRequest(ctx.Owner, ctx.Repo, index, opts)
if err != nil {
return err
}
if len(indices) > 1 {
fmt.Println(pr.HTMLURL)
} else {
print.PullDetails(pr, nil, nil)
}
}
return nil
}

View File

@ -1,60 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
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 pullFieldsFlag = flags.FieldsFlag(print.PullFields, []string{
"index", "title", "state", "author", "milestone", "updated", "labels",
})
// CmdPullsList represents a sub command of issues to list pulls
var CmdPullsList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List pull requests of the repository",
Description: `List pull requests of the repository`,
ArgsUsage: " ", // command does not accept arguments
Action: RunPullsList,
Flags: append([]cli.Flag{pullFieldsFlag}, flags.PRListingFlags...),
}
// RunPullsList return list of pulls
func RunPullsList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
state := gitea.StateOpen
switch ctx.String("state") {
case "all":
state = gitea.StateAll
case "open":
state = gitea.StateOpen
case "closed":
state = gitea.StateClosed
}
prs, _, err := ctx.Login.Client().ListRepoPullRequests(ctx.Owner, ctx.Repo, gitea.ListPullRequestsOptions{
State: state,
})
if err != nil {
return err
}
fields, err := pullFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
print.PullsList(prs, ctx.Output, fields)
return nil
}

View File

@ -1,62 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CmdPullsMerge merges a PR
var CmdPullsMerge = cli.Command{
Name: "merge",
Aliases: []string{"m"},
Usage: "Merge a pull request",
Description: "Merge a pull request",
ArgsUsage: "<pull index>",
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "style",
Aliases: []string{"s"},
Usage: "Kind of merge to perform: merge, rebase, squash, rebase-merge",
Value: "merge",
},
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
Usage: "Merge commit title",
},
&cli.StringFlag{
Name: "message",
Aliases: []string{"m"},
Usage: "Merge commit message",
},
}, flags.AllDefaultFlags...),
Action: func(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() != 1 {
// If no PR index is provided, try interactive mode
return interact.MergePull(ctx)
}
idx, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
return task.PullMerge(ctx.Login, ctx.Owner, ctx.Repo, idx, gitea.MergePullRequestOption{
Style: gitea.MergeStyle(ctx.String("style")),
Title: ctx.String("title"),
Message: ctx.String("message"),
})
},
}

View File

@ -1,43 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"fmt"
"strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdPullsReject requests changes to a PR
var CmdPullsReject = cli.Command{
Name: "reject",
Usage: "Request changes to a pull request",
Description: "Request changes to a pull request",
ArgsUsage: "<pull index> <reason>",
Action: func(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() < 2 {
return fmt.Errorf("Must specify a PR index and comment")
}
idx, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
comment := strings.Join(ctx.Args().Tail(), " ")
return task.CreatePullReview(ctx, idx, gitea.ReviewStateRequestChanges, comment, nil)
},
Flags: flags.AllDefaultFlags,
}

View File

@ -1,25 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdPullsReopen reopens a given closed pull request
var CmdPullsReopen = cli.Command{
Name: "reopen",
Aliases: []string{"open"},
Usage: "Change state of one or more pull requests to 'open'",
Description: `Change state of one or more pull requests to 'open'`,
ArgsUsage: "<pull index> [<pull index>...]",
Action: func(ctx *cli.Context) error {
var s = gitea.StateOpen
return editPullState(ctx, gitea.EditPullRequestOption{State: &s})
},
Flags: flags.AllDefaultFlags,
}

View File

@ -1,39 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
)
// CmdPullsReview starts an interactive review session
var CmdPullsReview = cli.Command{
Name: "review",
Usage: "Interactively review a pull request",
Description: "Interactively review a pull request",
ArgsUsage: "<pull index>",
Action: func(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() != 1 {
return fmt.Errorf("Must specify a PR index")
}
idx, err := utils.ArgToIndex(ctx.Args().First())
if err != nil {
return err
}
return interact.ReviewPull(ctx, idx)
},
Flags: flags.AllDefaultFlags,
}

View File

@ -1,31 +1,143 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// 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/flags"
"code.gitea.io/tea/cmd/releases"
"log"
"os"
"path/filepath"
"github.com/urfave/cli/v2"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli"
)
// CmdReleases represents to login a gitea server.
// ToDo: ReleaseDetails
var CmdReleases = cli.Command{
Name: "releases",
Aliases: []string{"release", "r"},
Category: catEntities,
Usage: "Manage releases",
Description: "Manage releases",
ArgsUsage: " ", // command does not accept arguments
Action: releases.RunReleasesList,
Subcommands: []*cli.Command{
&releases.CmdReleaseList,
&releases.CmdReleaseCreate,
&releases.CmdReleaseDelete,
&releases.CmdReleaseEdit,
&CmdReleaseAttachments,
Usage: "Create releases",
Description: `Create releases`,
Action: runReleases,
Subcommands: []cli.Command{
CmdReleaseCreate,
},
Flags: flags.AllDefaultFlags,
Flags: AllDefaultFlags,
}
func runReleases(ctx *cli.Context) error {
login, owner, repo := initCommand()
releases, err := login.Client().ListReleases(owner, repo)
if err != nil {
log.Fatal(err)
}
headers := []string{
"Tag-Name",
"Title",
"Published At",
"Tar URL",
}
var values [][]string
if len(releases) == 0 {
Output(outputValue, headers, values)
return nil
}
for _, release := range releases {
values = append(
values,
[]string{
release.TagName,
release.Title,
release.PublishedAt.Format("2006-01-02 15:04:05"),
release.TarURL,
},
)
}
Output(outputValue, headers, values)
return nil
}
// CmdReleaseCreate represents a sub command of Release to create release.
var CmdReleaseCreate = cli.Command{
Name: "create",
Usage: "Create a release",
Description: `Create a release`,
Action: runReleaseCreate,
Flags: append([]cli.Flag{
cli.StringFlag{
Name: "tag",
Usage: "Tag name",
},
cli.StringFlag{
Name: "target",
Usage: "Target refs, branch name or commit id",
},
cli.StringFlag{
Name: "title, t",
Usage: "Release title",
},
cli.StringFlag{
Name: "note, n",
Usage: "Release notes",
},
cli.BoolFlag{
Name: "draft, d",
Usage: "Is a draft",
},
cli.BoolFlag{
Name: "prerelease, p",
Usage: "Is a pre-release",
},
cli.StringSliceFlag{
Name: "asset, a",
Usage: "List of files to attach",
},
}, LoginRepoFlags...),
}
func runReleaseCreate(ctx *cli.Context) error {
login, owner, repo := initCommand()
release, err := login.Client().CreateRelease(owner, repo, gitea.CreateReleaseOption{
TagName: ctx.String("tag"),
Target: ctx.String("target"),
Title: ctx.String("title"),
Note: ctx.String("note"),
IsDraft: ctx.Bool("draft"),
IsPrerelease: ctx.Bool("prerelease"),
})
if err != nil {
if err.Error() == "409 Conflict" {
log.Fatal("error: There already is a release for this tag")
}
log.Fatal(err)
}
for _, asset := range ctx.StringSlice("asset") {
var file *os.File
if file, err = os.Open(asset); err != nil {
log.Fatal(err)
}
filePath := filepath.Base(asset)
if _, err = login.Client().CreateReleaseAttachment(owner, repo, release.ID, file, filePath); err != nil {
file.Close()
log.Fatal(err)
}
file.Close()
}
return nil
}

View File

@ -1,110 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package releases
import (
"fmt"
"net/http"
"os"
"path/filepath"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdReleaseCreate represents a sub command of Release to create release
var CmdReleaseCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create a release",
Description: `Create a release for a new or existing git tag`,
ArgsUsage: "[<tag>]",
Action: runReleaseCreate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "tag",
Usage: "Tag name. If the tag does not exist yet, it will be created by Gitea",
},
&cli.StringFlag{
Name: "target",
Usage: "Target branch name or commit hash. Defaults to the default branch of the repo",
},
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
Usage: "Release title",
},
&cli.StringFlag{
Name: "note",
Aliases: []string{"n"},
Usage: "Release notes",
},
&cli.BoolFlag{
Name: "draft",
Aliases: []string{"d"},
Usage: "Is a draft",
},
&cli.BoolFlag{
Name: "prerelease",
Aliases: []string{"p"},
Usage: "Is a pre-release",
},
&cli.StringSliceFlag{
Name: "asset",
Aliases: []string{"a"},
Usage: "Path to file attachment. Can be specified multiple times",
},
}, flags.AllDefaultFlags...),
}
func runReleaseCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
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{
TagName: tag,
Target: ctx.String("target"),
Title: ctx.String("title"),
Note: ctx.String("note"),
IsDraft: ctx.Bool("draft"),
IsPrerelease: ctx.Bool("prerelease"),
})
if err != nil {
if resp != nil && resp.StatusCode == http.StatusConflict {
return fmt.Errorf("There already is a release for this tag")
}
return err
}
for _, asset := range ctx.StringSlice("asset") {
var file *os.File
if file, err = os.Open(asset); err != nil {
return err
}
filePath := filepath.Base(asset)
if _, _, err = ctx.Login.Client().CreateReleaseAttachment(ctx.Owner, ctx.Repo, release.ID, file, filePath); err != nil {
file.Close()
return err
}
file.Close()
}
return nil
}

View File

@ -1,68 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package releases
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v2"
)
// CmdReleaseDelete represents a sub command of Release to delete a release
var CmdReleaseDelete = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "Delete one or more releases",
Description: `Delete one or more releases`,
ArgsUsage: "<release tag> [<release tag>...]",
Action: runReleaseDelete,
Flags: append([]cli.Flag{
&cli.BoolFlag{
Name: "confirm",
Aliases: []string{"y"},
Usage: "Confirm deletion (required)",
},
&cli.BoolFlag{
Name: "delete-tag",
Usage: "Also delete the git tag for this release",
},
}, flags.AllDefaultFlags...),
}
func runReleaseDelete(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
if !ctx.Args().Present() {
fmt.Println("Release tag needed to edit")
return nil
}
if !ctx.Bool("confirm") {
fmt.Println("Are you sure? Please confirm with -y or --confirm.")
return nil
}
for _, tag := range ctx.Args().Slice() {
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
if err != nil {
return err
}
_, err = client.DeleteRelease(ctx.Owner, ctx.Repo, release.ID)
if err != nil {
return err
}
if ctx.Bool("delete-tag") {
_, err = client.DeleteTag(ctx.Owner, ctx.Repo, tag)
return err
}
}
return nil
}

View File

@ -1,96 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package releases
import (
"fmt"
"strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
// CmdReleaseEdit represents a sub command of Release to edit releases
var CmdReleaseEdit = cli.Command{
Name: "edit",
Aliases: []string{"e"},
Usage: "Edit one or more releases",
Description: `Edit one or more releases`,
ArgsUsage: "<release tag> [<release tag>...]",
Action: runReleaseEdit,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "tag",
Usage: "Change Tag",
},
&cli.StringFlag{
Name: "target",
Usage: "Change Target",
},
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
Usage: "Change Title",
},
&cli.StringFlag{
Name: "note",
Aliases: []string{"n"},
Usage: "Change Notes",
},
&cli.StringFlag{
Name: "draft",
Aliases: []string{"d"},
Usage: "Mark as Draft [True/false]",
DefaultText: "true",
},
&cli.StringFlag{
Name: "prerelease",
Aliases: []string{"p"},
Usage: "Mark as Pre-Release [True/false]",
DefaultText: "true",
},
}, flags.AllDefaultFlags...),
}
func runReleaseEdit(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client()
var isDraft, isPre *bool
if ctx.IsSet("draft") {
isDraft = gitea.OptionalBool(strings.ToLower(ctx.String("draft"))[:1] == "t")
}
if ctx.IsSet("prerelease") {
isPre = gitea.OptionalBool(strings.ToLower(ctx.String("prerelease"))[:1] == "t")
}
if !ctx.Args().Present() {
fmt.Println("Release tag needed to edit")
return nil
}
for _, tag := range ctx.Args().Slice() {
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
if err != nil {
return err
}
_, _, err = client.EditRelease(ctx.Owner, ctx.Repo, release.ID, gitea.EditReleaseOption{
TagName: ctx.String("tag"),
Target: ctx.String("target"),
Title: ctx.String("title"),
Note: ctx.String("note"),
IsDraft: isDraft,
IsPrerelease: isPre,
})
if err != nil {
return err
}
}
return nil
}

View File

@ -1,63 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package releases
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"
)
// CmdReleaseList represents a sub command of Release to list releases
var CmdReleaseList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List Releases",
Description: "List Releases",
ArgsUsage: " ", // command does not accept arguments
Action: RunReleasesList,
Flags: append([]cli.Flag{
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
// RunReleasesList list releases
func RunReleasesList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
releases, _, err := ctx.Login.Client().ListReleases(ctx.Owner, ctx.Repo, gitea.ListReleasesOptions{
ListOptions: ctx.GetListOptions(),
})
if err != nil {
return err
}
print.ReleasesList(releases, ctx.Output)
return nil
}
func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) {
rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{
ListOptions: gitea.ListOptions{Page: -1},
})
if err != nil {
return nil, err
}
if len(rl) == 0 {
return nil, fmt.Errorf("Repo does not have any release")
}
for _, r := range rl {
if r.TagName == tag {
return r, nil
}
}
return nil, fmt.Errorf("Release tag does not exist")
}

View File

@ -1,59 +1,132 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// 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/repos"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils"
"log"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
"github.com/urfave/cli"
)
// CmdRepos represents to login a gitea server.
var CmdRepos = cli.Command{
Name: "repos",
Aliases: []string{"repo"},
Category: catEntities,
Usage: "Show repository details",
Description: "Show repository details",
ArgsUsage: "[<repo owner>/<repo name>]",
Action: runRepos,
Subcommands: []*cli.Command{
&repos.CmdReposList,
&repos.CmdReposSearch,
&repos.CmdRepoCreate,
&repos.CmdRepoCreateFromTemplate,
&repos.CmdRepoFork,
&repos.CmdRepoMigrate,
&repos.CmdRepoRm,
Usage: "Operate with repositories",
Description: `Operate with repositories`,
Action: runReposList,
Subcommands: []cli.Command{
CmdReposList,
},
Flags: repos.CmdReposListFlags,
Flags: LoginOutputFlags,
}
func runRepos(ctx *cli.Context) error {
if ctx.Args().Len() == 1 {
return runRepoDetail(ctx, ctx.Args().First())
}
return repos.RunReposList(ctx)
// CmdReposList represents a sub command of issues to list issues
var CmdReposList = cli.Command{
Name: "ls",
Usage: "List available repositories",
Description: `List available repositories`,
Action: runReposList,
Flags: append([]cli.Flag{
cli.StringFlag{
Name: "mode",
Usage: "Filter listed repositories based on mode, optional - fork, mirror, source",
},
cli.StringFlag{
Name: "org",
Usage: "Filter listed repositories based on organization, optional",
},
cli.StringFlag{
Name: "user",
Usage: "Filter listed repositories absed on user, optional",
},
}, LoginOutputFlags...),
}
func runRepoDetail(cmd *cli.Context, path string) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
repoOwner, repoName := utils.GetOwnerAndRepo(path, ctx.Owner)
repo, _, err := client.GetRepo(repoOwner, repoName)
if err != nil {
return err
// runReposList list repositories
func runReposList(ctx *cli.Context) error {
login := initCommandLoginOnly()
mode := ctx.String("mode")
org := ctx.String("org")
user := ctx.String("user")
var rps []*gitea.Repository
var err error
if org != "" {
rps, err = login.Client().ListOrgRepos(org)
} else if user != "" {
rps, err = login.Client().ListUserRepos(user)
} else {
rps, err = login.Client().ListMyRepos()
}
topics, _, err := client.ListRepoTopics(repoOwner, repoName, gitea.ListRepoTopicsOptions{})
if err != nil {
return err
log.Fatal(err)
}
print.RepoDetails(repo, topics)
var repos []*gitea.Repository
if mode == "" {
repos = rps
} else if mode == "fork" {
for _, rp := range rps {
if rp.Fork == true {
repos = append(repos, rp)
}
}
} else if mode == "mirror" {
for _, rp := range rps {
if rp.Mirror == true {
repos = append(repos, rp)
}
}
} else if mode == "source" {
for _, rp := range rps {
if rp.Mirror != true && rp.Fork != true {
repos = append(repos, rp)
}
}
} else {
log.Fatal("Unknown mode: ", mode, "\nUse one of the following:\n- fork\n- mirror\n- source\n")
return nil
}
if len(rps) == 0 {
log.Fatal("No repositories found", rps)
return nil
}
headers := []string{
"Name",
"Type",
"SSH",
"Owner",
}
var values [][]string
for _, rp := range repos {
var mode = "source"
if rp.Fork {
mode = "fork"
}
if rp.Mirror {
mode = "mirror"
}
values = append(
values,
[]string{
rp.FullName,
mode,
rp.SSHURL,
rp.Owner.UserName,
},
)
}
Output(outputValue, headers, values)
return nil
}

View File

@ -1,145 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
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"
)
// CmdRepoCreate represents a sub command of repos to create one
var CmdRepoCreate = cli.Command{
Name: "create",
Aliases: []string{"c"},
Usage: "Create a repository",
Description: "Create a repository",
ArgsUsage: " ", // command does not accept arguments
Action: runRepoCreate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{""},
Required: true,
Usage: "name of new repo",
},
&cli.StringFlag{
Name: "owner",
Aliases: []string{"O"},
Required: false,
Usage: "name of repo owner",
},
&cli.BoolFlag{
Name: "private",
Required: false,
Value: false,
Usage: "make repo private",
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"desc"},
Required: false,
Usage: "add description to repo",
},
&cli.BoolFlag{
Name: "init",
Required: false,
Value: false,
Usage: "initialize repo",
},
&cli.StringFlag{
Name: "labels",
Required: false,
Usage: "name of label set to add",
},
&cli.StringFlag{
Name: "gitignores",
Aliases: []string{"git"},
Required: false,
Usage: "list of gitignore templates (need --init)",
},
&cli.StringFlag{
Name: "license",
Required: false,
Usage: "add license (need --init)",
},
&cli.StringFlag{
Name: "readme",
Required: false,
Usage: "use readme template (need --init)",
},
&cli.StringFlag{
Name: "branch",
Required: false,
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...),
}
func runRepoCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
var (
repo *gitea.Repository
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{
Name: ctx.String("name"),
Description: ctx.String("description"),
Private: ctx.Bool("private"),
AutoInit: ctx.Bool("init"),
IssueLabels: ctx.String("labels"),
Gitignores: ctx.String("gitignores"),
License: ctx.String("license"),
Readme: ctx.String("readme"),
DefaultBranch: ctx.String("branch"),
Template: ctx.Bool("template"),
TrustModel: trustmodel,
}
if len(ctx.String("owner")) != 0 {
repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts)
} else {
repo, _, err = client.CreateRepo(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
}

View File

@ -1,120 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
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
}

View File

@ -1,85 +0,0 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
import (
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
)
// CmdRepoRm represents a sub command of repos to delete an existing repo
var CmdRepoRm = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "Delete an existing repository",
Description: "Removes a repository from Create a repository from an existing repo",
ArgsUsage: " ", // command does not accept arguments
Action: runRepoDelete,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{""},
Required: true,
Usage: "name of the repo",
},
&cli.StringFlag{
Name: "owner",
Aliases: []string{"O"},
Required: false,
Usage: "owner of the repo",
},
&cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Required: false,
Value: false,
Usage: "Force the deletion and don't ask for confirmation",
},
}, flags.LoginOutputFlags...),
}
func runRepoDelete(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
var owner string
if ctx.IsSet("owner") {
owner = ctx.String("owner")
} else {
owner = ctx.Login.User
}
repoName := ctx.String("name")
repoSlug := fmt.Sprintf("%s/%s", owner, repoName)
if !ctx.Bool("force") {
var enteredRepoSlug string
promptRepoName := &survey.Input{
Message: fmt.Sprintf("Confirm the deletion of the repository '%s' by typing its name: ", repoSlug),
}
if err := survey.AskOne(promptRepoName, &enteredRepoSlug, survey.WithValidator(survey.Required)); err != nil {
return err
}
if enteredRepoSlug != repoSlug {
return fmt.Errorf("Entered wrong repository name '%s', expected '%s'", enteredRepoSlug, repoSlug)
}
}
_, err := client.DeleteRepo(owner, repoName)
if err != nil {
return err
}
fmt.Printf("Successfully deleted %s/%s\n", owner, repoName)
return nil
}

View File

@ -1,36 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
import (
"fmt"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
)
var typeFilterFlag = cli.StringFlag{
Name: "type",
Aliases: []string{"T"},
Required: false,
Usage: "Filter by type: fork, mirror, source",
}
func getTypeFilter(ctx *cli.Context) (filter gitea.RepoType, err error) {
t := ctx.String("type")
filter = gitea.RepoTypeNone
switch t {
case "":
filter = gitea.RepoTypeNone
case "fork":
filter = gitea.RepoTypeFork
case "mirror":
filter = gitea.RepoTypeMirror
case "source":
filter = gitea.RepoTypeSource
default:
err = fmt.Errorf("invalid repo type '%s'. valid: fork, mirror, source", t)
}
return
}

View File

@ -1,58 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
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
}

View File

@ -1,116 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
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 repoFieldsFlag = flags.FieldsFlag(print.RepoFields, []string{
"owner", "name", "type", "ssh",
})
// CmdReposListFlags contains all flags needed for repo listing
var CmdReposListFlags = append([]cli.Flag{
&cli.BoolFlag{
Name: "watched",
Aliases: []string{"w"},
Required: false,
Usage: "List your watched repos instead",
},
&cli.BoolFlag{
Name: "starred",
Aliases: []string{"s"},
Required: false,
Usage: "List your starred repos instead",
},
repoFieldsFlag,
&typeFilterFlag,
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.LoginOutputFlags...)
// CmdReposList represents a sub command of repos to list them
var CmdReposList = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "List repositories you have access to",
Description: "List repositories you have access to",
Action: RunReposList,
Flags: CmdReposListFlags,
}
// RunReposList list repositories
func RunReposList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
typeFilter, err := getTypeFilter(cmd)
if err != nil {
return err
}
var rps []*gitea.Repository
if ctx.Bool("starred") {
user, _, err := client.GetMyUserInfo()
if err != nil {
return err
}
rps, _, err = client.SearchRepos(gitea.SearchRepoOptions{
ListOptions: ctx.GetListOptions(),
StarredByUserID: user.ID,
})
} else if ctx.Bool("watched") {
rps, _, err = client.GetMyWatchedRepos() // TODO: this does not expose pagination..
} else {
rps, _, err = client.ListMyRepos(gitea.ListReposOptions{
ListOptions: ctx.GetListOptions(),
})
}
if err != nil {
return err
}
reposFiltered := rps
if typeFilter != gitea.RepoTypeNone {
reposFiltered = filterReposByType(rps, typeFilter)
}
fields, err := repoFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
print.ReposList(reposFiltered, ctx.Output, fields)
return nil
}
func filterReposByType(repos []*gitea.Repository, t gitea.RepoType) []*gitea.Repository {
var filtered []*gitea.Repository
for _, r := range repos {
switch t {
case gitea.RepoTypeFork:
if !r.Fork {
continue
}
case gitea.RepoTypeMirror:
if !r.Mirror {
continue
}
case gitea.RepoTypeSource:
if r.Fork || r.Mirror {
continue
}
}
filtered = append(filtered, r)
}
return filtered
}

View File

@ -1,168 +0,0 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
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"
)
// CmdRepoMigrate represents a sub command of repos to migrate one
var CmdRepoMigrate = cli.Command{
Name: "migrate",
Aliases: []string{"m"},
Usage: "Migrate a repository",
Description: "Migrate a repository and or mirror it.",
ArgsUsage: " ", // command does not accept arguments
Action: runRepoMigrate,
Flags: append([]cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "Name of the repository",
Required: true,
},
&cli.StringFlag{
Name: "owner",
Usage: "Owner of the repository",
Required: true,
},
&cli.StringFlag{
Name: "clone-url",
Usage: "Clone URL of the repository",
Required: true,
},
&cli.StringFlag{
Name: "service",
Usage: string("Service to migrate from. Supported services are: " + gitea.GitServicePlain +
", " + gitea.GitServiceGitea + ", " + gitea.GitServiceGitlab + ", " + gitea.GitServiceGogs),
Required: true,
},
&cli.BoolFlag{
Name: "mirror",
Usage: "Mirror the repository",
},
&cli.BoolFlag{
Name: "private",
Usage: "Make the repository private",
},
&cli.BoolFlag{
Name: "template",
Usage: "Make the repository a template",
},
&cli.BoolFlag{
Name: "wiki",
Usage: "Copy the wiki",
},
&cli.BoolFlag{
Name: "issues",
Usage: "Copy the issues",
},
&cli.BoolFlag{
Name: "pull-requests",
Usage: "Copy the pull requests",
},
&cli.BoolFlag{
Name: "releases",
Usage: "Copy the releases",
},
&cli.BoolFlag{
Name: "milestones",
Usage: "Copy the milestones",
},
&cli.StringFlag{
Name: "mirror-interval",
Usage: "Interval to mirror the repository.",
},
&cli.BoolFlag{
Name: "lfs",
Usage: "Copy the LFS objects",
},
&cli.StringFlag{
Name: "lfs-endpoint",
Usage: "LFS endpoint to use",
},
&cli.StringFlag{
Name: "auth-user",
Usage: "Username to use for authentication.",
},
&cli.StringFlag{
Name: "auth-password",
Usage: "Password to use for authentication.",
},
&cli.StringFlag{
Name: "auth-token",
Usage: "Token to use for authentication.",
},
}, flags.LoginOutputFlags...),
}
func runRepoMigrate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
var (
repo *gitea.Repository
err error
service gitea.GitServiceType
)
if ctx.IsSet("service") {
switch ctx.String("service") {
case "git":
service = gitea.GitServicePlain
case "gitea":
service = gitea.GitServiceGitea
case "gitlab":
service = gitea.GitServiceGitlab
case "gogs":
service = gitea.GitServiceGogs
case "github":
service = gitea.GitServiceGithub
default:
return fmt.Errorf("unknown git service type '%s'", ctx.String("service"))
}
}
opts := gitea.MigrateRepoOption{
RepoName: ctx.String("name"),
RepoOwner: ctx.String("owner"),
CloneAddr: ctx.String("clone-url"),
Service: service,
AuthUsername: ctx.String("auth-user"),
AuthPassword: ctx.String("auth-password"),
AuthToken: ctx.String("auth-token"),
Mirror: ctx.Bool("mirror"),
Private: ctx.Bool("private"),
Description: ctx.String("description"),
Wiki: ctx.Bool("wiki"),
Milestones: ctx.Bool("milestones"),
Labels: ctx.Bool("labels"),
Issues: ctx.Bool("issues"),
PullRequests: ctx.Bool("pull-requests"),
Releases: ctx.Bool("releases"),
MirrorInterval: ctx.String("mirror-interval"),
LFS: ctx.Bool("lfs"),
LFSEndpoint: ctx.String("lfs-endpoint"),
}
repo, _, err = client.MigrateRepo(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
}

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