1 Commits
main ... v0.7.1

Author SHA1 Message Date
9fb8b883f3 darwin/arm64 & update s3 bucket 2021-08-26 18:15:51 -04:00
1814 changed files with 430724 additions and 9621 deletions

View File

@ -1,20 +0,0 @@
{
"name": "Tea DevContainer",
"image": "mcr.microsoft.com/devcontainers/go:1.24-bullseye",
"features": {
"ghcr.io/devcontainers/features/git-lfs:1.2.5": {}
},
"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

189
.drone.yml Normal file
View File

@ -0,0 +1,189 @@
---
kind: pipeline
name: default
platform:
os: linux
arch: amd64
steps:
- name: build
pull: always
image: golang:1.16
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
image: golang:1.16
commands:
- make unit-test-coverage
settings:
group: test
when:
branch:
- master
event:
- push
- pull_request
- name: release-test
image: golang:1.16
commands:
- make test
settings:
group: test
when:
branch:
- "release/*"
event:
- push
- pull_request
- name: tag-test
pull: always
image: golang:1.16
commands:
- make test
settings:
group: test
when:
event:
- tag
- name: static
image: golang:1.16
environment:
GOPROXY: https://goproxy.cn
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: gitea-artifacts
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: gitea-artifacts
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: gitea-artifacts
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-release: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

1
.envrc
View File

@ -1 +0,0 @@
use flake

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,76 +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 }}
GORELEASER_FORCE_TOKEN: 'gitea'
GPGSIGN_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }}
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
GITEA_TOKEN: ${{ secrets.RELEASE_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,81 +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 }}
- 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 }}
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: Get tag version without v
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
- 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:${{ env.VERSION }}

View File

@ -1,64 +0,0 @@
name: check-and-test
on:
- pull_request
jobs:
govulncheck_job:
runs-on: ubuntu-latest
name: Run govulncheck
steps:
- id: govulncheck
uses: golang/govulncheck-action@v1
with:
go-version-file: 'go.mod'
check-and-test:
runs-on: ubuntu-latest
env:
HTTP_PROXY: ""
GITEA_TEA_TEST_URL: "http://gitea:3000"
GITEA_TEA_TEST_USERNAME: "test01"
GITEA_TEA_TEST_PASSWORD: "test01"
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
- run: curl --noproxy "*" http://gitea:3000/api/v1/version # verify connection to instance
- name: test and coverage
run: |
make test
make unit-test-coverage
services:
gitea:
image: docker.gitea.com/gitea:1.24.5
cmd:
- bash
- -c
- >-
mkdir -p /tmp/conf/
&& mkdir -p /tmp/data/
&& echo "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT = true" > /tmp/conf/app.ini
&& echo "[security]" >> /tmp/conf/app.ini
&& echo "INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE1NTg4MzY4ODB9.LoKQyK5TN_0kMJFVHWUW0uDAyoGjDP6Mkup4ps2VJN4" >> /tmp/conf/app.ini
&& echo "INSTALL_LOCK = true" >> /tmp/conf/app.ini
&& echo "SECRET_KEY = 2crAW4UANgvLipDS6U5obRcFosjSJHQANll6MNfX7P0G3se3fKcCwwK3szPyGcbo" >> /tmp/conf/app.ini
&& echo "PASSWORD_COMPLEXITY = off" >> /tmp/conf/app.ini
&& echo "[database]" >> /tmp/conf/app.ini
&& echo "DB_TYPE = sqlite3" >> /tmp/conf/app.ini
&& echo "[repository]" >> /tmp/conf/app.ini
&& echo "ROOT = /tmp/data/" >> /tmp/conf/app.ini
&& echo "[server]" >> /tmp/conf/app.ini
&& echo "ROOT_URL = http://gitea:3000" >> /tmp/conf/app.ini
&& gitea migrate -c /tmp/conf/app.ini
&& gitea admin user create --username=test01 --password=test01 --email=test01@gitea.io --admin=true --must-change-password=false --access-token -c /tmp/conf/app.ini
&& gitea web -c /tmp/conf/app.ini

13
.gitignore vendored
View File

@ -1,19 +1,8 @@
/tea tea
/gitea-vet /gitea-vet
/gitea-vet.exe
.idea/ .idea/
.history/ .history/
dist/ dist/
.vscode/ .vscode/
vendor/
coverage.out
dist/
# Nix-specific
.direnv/
result
result-*

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 code.gitea.io/tea/cmd.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.increment-decrement]
[rule.var-naming] [rule.var-naming]
[rule.var-declaration] [rule.var-declaration]
[rule.package-comments]
[rule.range] [rule.range]
[rule.receiver-naming] [rule.receiver-naming]
[rule.time-naming] [rule.time-naming]

View File

@ -1,78 +1,5 @@
# Changelog # Changelog
## [v0.9.1](https://gitea.com/gitea/tea/releases/tag/v0.9.1) - 2023-02-15
* BUGFIXES
* Print pull dont crash if it has TeamReviewRequests (#517)
## [v0.9.0](https://gitea.com/gitea/tea/releases/tag/v0.9.0) - 2022-09-13
* BREAKING
* Rename master branch to main (#495)
* Return RFC3339 UTC timestamps for machine-readable output (#470)
* FEATURES
* Allow editing multiline prompts with external text editor (#429)
* Add `tea admin user list` (#427)
* Add `tea whoami` command (#426)
* Add `tea org create <name>` (#420)
* Add `tea clone` (#411)
* Add `tea repo fork` (#410)
* Add `tea repo create-from-template` (#408)
* BUGFIXES
* Fetch all items where needed. (#475)
* Fix running in repos without remote (#472)
* Add TSV to machine-readable formats (#467)
* Fix create milestone with deadline bug (#462)
* Fix resolving of URLs in markdown (#401)
* ENHANCEMENTS
* Don't emit ANSI sequences when not emitting to TTY for markdown (#491)
* Show more version info (#486)
* Add preference `flag_defaults.remote`, refactor (#466)
* Add `--fields` to notification & milestone listings (#422)
* PR listing: add --fields & expose additional fields (#415)
* Add more flags to `tea repo create` (#409)
* Implement more issue filters (#400)
* MISC
* Simplify build & update installation instructions (#437)
* Clarify command descriptions when no arguments are taken (#496)
* Improve Documentation (#433)
* Use golang v1.18 and drop vendor folder (#478)
* Correct spelling of "wether" to "whether" in usage output (#453)
## [v0.8.0](https://gitea.com/gitea/tea/releases/tag/v0.8.0) - 2021-09-22
* BREAKING
* `tea notifications --all` has moved to `tea notifications --mine` (#389)
* `tea notifications` now only works with the context of a remote repo. (#389)
To run this outside of a local git dir, run either tea n `--mine` or `tea n --repo <my/repo>`
* FEATURES
* Add `tea pr merge` (#348)
* BUGFIXES
* Don't skip reading the local repo when `--repo` specifies a repo slug (#398)
* Fix adding login without token on private instances (#392)
* Correctly match login by ssh host with port (#391)
* Fix printing issue deadline (#388)
* Return useful error on wrong sshkey path (#374)
* Fix parsing of `--description` for issue/pr create (#371)
* Add missing flags (#369)
* Check negative limit command parameter (#358) (#359)
* Add missing flags to org & labels subcommands (#357)
* ENHANCEMENTS
* Don't require a body for comment PR reviews (#399)
* Accept more main branch names for login detection (#396)
* Make local repo optional for `tea pr create`(#393)
* Notifications Add State Field (#384)
* Improve error messages (#370)
* Add tab completion for fish shell (#364)
* Text editor selection: follow unix defacto standards (#356)
* MISC
* Update Dependencies (#390)
## [v0.7.1](https://gitea.com/gitea/tea/releases/tag/v0.7.1) - 2021-08-27
* BUILD
* Enable release builds for darwin/arm64 (#360)
## [v0.7.0](https://gitea.com/gitea/tea/releases/tag/v0.7.0) - 2021-03-12 ## [v0.7.0](https://gitea.com/gitea/tea/releases/tag/v0.7.0) - 2021-03-12
* BREAKING * BREAKING

View File

@ -7,6 +7,7 @@
- [Bug reports](#bug-reports) - [Bug reports](#bug-reports)
- [Discuss your design](#discuss-your-design) - [Discuss your design](#discuss-your-design)
- [Testing redux](#testing-redux) - [Testing redux](#testing-redux)
- [Vendoring](#vendoring)
- [Code review](#code-review) - [Code review](#code-review)
- [Styleguide](#styleguide) - [Styleguide](#styleguide)
- [Sign-off your work](#sign-off-your-work) - [Sign-off your work](#sign-off-your-work)
@ -56,13 +57,27 @@ high-level discussions.
## Testing redux ## Testing redux
Before sending code out for review, run all the test by executing: `make test` Before sending code out for review, run all the test by execting: `make test`
Since TEA is an cli tool it should be obvious to test your feature locally first. Since TEA is an cli tool it should be obvious to test your feature localy first.
## 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).
## Code review ## Code review
Changes to TEA must be reviewed before they are accepted—no matter who Changes to TEA must be reviewed before they are accepted—no matter who
makes the change, even if they are an owner or a maintainer. We use Gitea's makes the change, even if they are an owner or a maintainer. We use Giteas's
pull request & review workflow to do that. Gitea ensure every PR is reviewed by at least 2 maintainers. pull request & review workflow to do that. Gitea ensure every PR is reviewed by at least 2 maintainers.
Please try to make your pull request easy to review for us. And, please read Please try to make your pull request easy to review for us. And, please read
@ -103,8 +118,8 @@ Some of the key points:
- Always make sure that the help texts are properly set, and as concise as possible. - Always make sure that the help texts are properly set, and as concise as possible.
### Internal Module Structure ### Internal Module Structure
- `cmd`: only contains command/flag options for `urfave/cli` - `cmd`: only contains comand/flag options for `urfave/cli`
- subcommands are in a subpackage named after its parent command - subcomands are in a subpackage named after its parent comand
- `modules/task`: contain func for doing something with gitea - `modules/task`: contain func for doing something with gitea
(e.g. create token by user/passwd) (e.g. create token by user/passwd)
- `modules/print`: contain all functions that print to stdout - `modules/print`: contain all functions that print to stdout
@ -160,7 +175,7 @@ maintainers](MAINTAINERS). Every PR **MUST** be reviewed by at least
two maintainers (or owners) before it can get merged. A maintainer two maintainers (or owners) before it can get merged. A maintainer
should be a contributor of Gitea (or Gogs) and contributed at least should be a contributor of Gitea (or Gogs) and contributed at least
4 accepted PRs. A contributor should apply as a maintainer in the 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 or the team maintainers may invite the contributor. A maintainer
should spend some time on code reviews. If a maintainer has no should spend some time on code reviews. If a maintainer has no
time to do that, they should apply to leave the maintainers team time to do that, they should apply to leave the maintainers team
@ -193,7 +208,7 @@ https://help.github.com/articles/securing-your-account-with-two-factor-authentic
After the election, the new owners should proactively agree After the election, the new owners should proactively agree
with our [CONTRIBUTING](CONTRIBUTING.md) requirements in the 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: words to speak:
``` ```
@ -221,8 +236,8 @@ Code that you contribute should use the standard copyright header:
``` ```
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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.
``` ```
Files in the repository contain copyright from the year they are added Files in the repository contain copyright from the year they are added

View File

@ -1,12 +0,0 @@
FROM docker.io/chainguard/go:latest AS build
COPY . /build/
WORKDIR /build
RUN make build && mkdir -p /app/.config/tea
FROM docker.io/chainguard/busybox:latest-glibc
COPY --from=build /build/tea /bin/tea
COPY --from=build --chown=65532:65532 /app /app
VOLUME [ "/app" ]
ENV HOME="/app"
ENTRYPOINT ["/bin/sh", "-c"]
CMD [ "tea" ]

63
FEATURE-COMPARISON.md Normal file
View File

@ -0,0 +1,63 @@
# 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) 2016 The Gitea Authors
Copyright (c) 2015 The Gogs Authors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

138
Makefile
View File

@ -1,12 +1,31 @@
DIST := dist DIST := dist
IMPORT := code.gitea.io/tea
export GO111MODULE=on
GO ?= go GO ?= go
SED_INPLACE := sed -i
SHASUM ?= shasum -a 256 SHASUM ?= shasum -a 256
export PATH := $($(GO) env GOPATH)/bin:$(PATH) export PATH := $($(GO) env GOPATH)/bin:$(PATH)
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 GOFMT ?= gofmt -s
GOFLAGS := -i -v
EXTRA_GOFLAGS ?=
MAKE_VERSION := $(shell make -v | head -n 1)
ifneq ($(DRONE_TAG),) ifneq ($(DRONE_TAG),)
VERSION ?= $(subst v,,$(DRONE_TAG)) VERSION ?= $(subst v,,$(DRONE_TAG))
TEA_VERSION ?= $(VERSION) TEA_VERSION ?= $(VERSION)
@ -14,37 +33,33 @@ else
ifneq ($(DRONE_BRANCH),) ifneq ($(DRONE_BRANCH),)
VERSION ?= $(subst release/v,,$(DRONE_BRANCH)) VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
else else
VERSION ?= main VERSION ?= master
endif endif
TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
endif endif
TEA_VERSION_TAG ?= $(shell sed 's/+/_/' <<< $(TEA_VERSION))
TAGS ?= LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)"
SDK ?= $(shell $(GO) list -f '{{.Version}}' -m code.gitea.io/sdk/gitea)
LDFLAGS := -X "code.gitea.io/tea/cmd.Version=$(TEA_VERSION)" -X "code.gitea.io/tea/cmd.Tags=$(TAGS)" -X "code.gitea.io/tea/cmd.SDK=$(SDK)" -s -w
# override to allow passing additional goflags via make CLI PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
override GOFLAGS := $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)'
PACKAGES ?= $(shell $(GO) list ./...)
SOURCES ?= $(shell find . -name "*.go" -type f) SOURCES ?= $(shell find . -name "*.go" -type f)
# OS specific vars. TAGS ?=
ifeq ($(OS), Windows_NT) ifeq ($(OS), Windows_NT)
EXECUTABLE := tea.exe EXECUTABLE := tea.exe
VET_TOOL := gitea-vet.exe
else else
EXECUTABLE := tea EXECUTABLE := tea
VET_TOOL := gitea-vet
endif endif
# $(call strip-suffix,filename)
strip-suffix = $(firstword $(subst ., ,$(1)))
.PHONY: all .PHONY: all
all: build all: build
.PHONY: clean .PHONY: clean
clean: clean:
$(GO) clean -i ./... $(GO) clean -mod=vendor -i ./...
rm -rf $(EXECUTABLE) $(DIST) rm -rf $(EXECUTABLE) $(DIST)
.PHONY: fmt .PHONY: fmt
@ -54,22 +69,31 @@ fmt:
.PHONY: vet .PHONY: vet
vet: vet:
# Default vet # Default vet
$(GO) vet $(PACKAGES) $(GO) vet -mod=vendor $(PACKAGES)
# Custom vet # Custom vet
$(GO) build code.gitea.io/gitea-vet $(GO) build -mod=vendor code.gitea.io/gitea-vet
$(GO) vet -vettool=$(VET_TOOL) $(PACKAGES) $(GO) vet -vettool=gitea-vet $(PACKAGES)
.PHONY: lint .PHONY: lint
lint: install-lint-tools lint:
$(GO) run github.com/mgechev/revive@v1.3.2 -config .revive.toml ./... || exit 1 @hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
cd /tmp && $(GO) get -u github.com/mgechev/revive; \
fi
revive -config .revive.toml -exclude=./vendor/... ./... || exit 1
.PHONY: misspell-check .PHONY: misspell-check
misspell-check: install-lint-tools misspell-check:
$(GO) run github.com/client9/misspell/cmd/misspell@latest -error -i unknwon,destory $(GOFILES) @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
cd /tmp && $(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -error -i unknwon,destory $(GOFILES)
.PHONY: misspell .PHONY: misspell
misspell: install-lint-tools misspell:
$(GO) run github.com/client9/misspell/cmd/misspell@latest -w -i unknwon $(GOFILES) @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
cd /tmp && $(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -w -i unknwon $(GOFILES)
.PHONY: fmt-check .PHONY: fmt-check
fmt-check: fmt-check:
@ -81,53 +105,61 @@ fmt-check:
exit 1; \ exit 1; \
fi; fi;
.PHONY: docs
docs:
$(GO) run docs/docs.go --out docs/CLI.md
.PHONY: docs-check
docs-check:
@DIFF=$$($(GO) run docs/docs.go | diff docs/CLI.md -); \
if [ -n "$$DIFF" ]; then \
echo "Please run 'make docs' and commit the result:"; \
echo "$$DIFF"; \
exit 1; \
fi;
.PHONY: test .PHONY: test
test: test:
$(GO) test -tags='sqlite sqlite_unlock_notify' $(PACKAGES) $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' $(PACKAGES)
.PHONY: unit-test-coverage .PHONY: unit-test-coverage
unit-test-coverage: unit-test-coverage:
$(GO) test -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: tidy .PHONY: vendor
tidy: vendor:
$(GO) mod tidy $(GO) mod tidy && $(GO) mod vendor
.PHONY: test-vendor
test-vendor: vendor
@diff=$$(git diff vendor/); \
if [ -n "$$diff" ]; then \
echo "Please run 'make vendor' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
.PHONY: check .PHONY: check
check: test check: test
.PHONY: install .PHONY: install
install: $(SOURCES) install: $(wildcard *.go)
@echo "installing to $(shell $(GO) env GOPATH)/bin/$(EXECUTABLE)" $(GO) install -mod=vendor -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
$(GO) install -v $(BUILDMODE) $(GOFLAGS)
.PHONY: build .PHONY: build
build: $(EXECUTABLE) build: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES) $(EXECUTABLE): $(SOURCES)
$(GO) build $(BUILDMODE) $(GOFLAGS) -o $@ $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
.PHONY: build-image .PHONY: release
build-image: release: release-dirs release-os release-compress release-check
docker build --build-arg VERSION=$(TEA_VERSION) -t gitea/tea:$(TEA_VERSION_TAG) .
install-lint-tools: .PHONY: release-dirs
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ release-dirs:
$(GO) install github.com/mgechev/revive@v1.3.2; \ mkdir -p $(DIST)/binaries $(DIST)/release
.PHONY: release-os
release-os:
@hash gox > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
cd /tmp && $(GO) get -u github.com/mitchellh/gox; \
fi fi
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ CGO_ENABLED=0 gox -verbose -cgo=false -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -osarch='!darwin/386 !darwin/arm' -os="windows linux darwin" -arch="386 amd64 arm arm64" -output="$(DIST)/release/tea-$(VERSION)-{{.OS}}-{{.Arch}}"
$(GO) install github.com/client9/misspell/cmd/misspell@latest; \
.PHONY: release-compress
release-compress:
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
fi fi
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
.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;

139
README.md
View File

@ -1,37 +1,26 @@
# <img alt='tea logo' src='https://gitea.com/repo-avatars/550-80a3a8c2ab0e2c2d69f296b7f8582485' height="40"/> *T E A* # <img alt='' src='https://gitea.com/repo-avatars/550-80a3a8c2ab0e2c2d69f296b7f8582485' height="40"/> *T E A*
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Release](https://raster.shields.io/badge/dynamic/json.svg?label=release&url=https://gitea.com/api/v1/repos/gitea/tea/releases&query=$[0].tag_name)](https://gitea.com/gitea/tea/releases) [![Build Status](https://drone.gitea.com/api/badges/gitea/tea/status.svg)](https://drone.gitea.com/gitea/tea) [![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea) [![Go Report Card](https://goreportcard.com/badge/code.gitea.io/tea)](https://goreportcard.com/report/code.gitea.io/tea) [![GoDoc](https://godoc.org/code.gitea.io/tea?status.svg)](https://godoc.org/code.gitea.io/tea)
[![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 ### The official CLI for Gitea
![demo gif](./demo.gif) ![demo gif](./demo.gif)
``` ```
NAME:
tea - command line tool to interact with Gitea tea - command line tool to interact with Gitea
version 0.7.0-preview
USAGE: USAGE
tea [global options] [command [command options]] tea command [subcommand] [command options] [arguments...]
VERSION: DESCRIPTION
Version: 0.10.1+15-g8876fe3 golang: 1.25.0 go-sdk: v0.21.0 tea is a productivity helper for Gitea. It can be used to manage most entities on one
or multiple Gitea instances and provides local helpers like 'tea pull checkout'.
tea makes use of context provided by the repository in $PWD if available, but is still
usable independently of $PWD. Configuration is persisted in $XDG_CONFIG_HOME/tea.
DESCRIPTION: COMMANDS
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 help, h Shows a list of commands or help for one command
ENTITIES: ENTITIES:
issues, issue, i List, create and update issues issues, issue, i List, create and update issues
pulls, pull, pr Manage and checkout pull requests pulls, pull, pr Manage and checkout pull requests
@ -41,26 +30,17 @@ COMMANDS:
times, time, t Operate on tracked times of a repository's issues & pulls times, time, t Operate on tracked times of a repository's issues & pulls
organizations, organization, org List, create, delete organizations organizations, organization, org List, create, delete organizations
repos, repo Show repository details repos, repo Show repository details
branches, branch, b Consult branches
comment, c Add a comment to an issue / pr
HELPERS: HELPERS:
open, o Open something of the repository in web browser open, o Open something of the repository in web browser
notifications, notification, n Show notifications notifications, notification, n Show notifications
clone, C Clone a repository locally
MISCELLANEOUS:
whoami Show current logged in user
admin, a Operations requiring admin access on the Gitea instance
SETUP: SETUP:
logins, login Log in to a Gitea server logins, login Log in to a Gitea server
logout Log out from a Gitea server logout Log out from a Gitea server
shellcompletion, autocomplete Install shell completion for tea
GLOBAL OPTIONS: OPTIONS
--debug, --vvv Enable debug mode (default: false) --help, -h show help (default: false)
--help, -h show help --version, -v print the version (default: false)
--version, -v print the version
EXAMPLES EXAMPLES
tea login add # add a login once to get started tea login add # add a login once to get started
@ -78,12 +58,12 @@ EXAMPLES
tea open milestones # open web ui for milestones tea open milestones # open web ui for milestones
# send gitea desktop notifications every 5 minutes (bash + libnotify) # send gitea desktop notifications every 5 minutes (bash + libnotify)
while :; do tea notifications --mine -o simple | xargs -i notify-send {}; sleep 300; done while :; do tea notifications --all -o simple | xargs -i notify-send {}; sleep 300; done
ABOUT ABOUT
Written & maintained by The Gitea Authors. 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. 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. More info about Gitea itself on https://gitea.io.
``` ```
- [Compare features with other git forge CLIs](./FEATURE-COMPARISON.md) - [Compare features with other git forge CLIs](./FEATURE-COMPARISON.md)
@ -94,92 +74,34 @@ ABOUT
There are different ways to get `tea`: There are different ways to get `tea`:
1. Install via your system package manager: 1. Install via your system package manager:
- macOS via `brew` (official): - macOS via `brew` (gitea-maintained):
```sh ```sh
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
brew install tea brew install tea
``` ```
- arch linux ([tea](https://archlinux.org/packages/extra/x86_64/tea/), thirdparty) - arch linux ([gitea-tea](https://aur.archlinux.org/packages/gitea-tea), thirdparty)
- alpine linux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge), thirdparty) - alpine linux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge), thirdparty)
- Windows via `MSYS2` ([tea](https://packages.msys2.org/base/mingw-w64-tea), thirdparty)
2. Use the prebuilt binaries from [dl.gitea.com](https://dl.gitea.com/tea/) 2. Use the prebuilt binaries from [dl.gitea.io](https://dl.gitea.io/tea/)
3. Install from source: [see *Compilation*](#compilation) 3. Install from source (go 1.13 or newer is required):
```sh
go get code.gitea.io/tea
go install code.gitea.io/tea
```
4. Docker (thirdparty): [tgerczei/tea](https://hub.docker.com/r/tgerczei/tea) 4. Docker (thirdparty): [tgerczei/tea](https://hub.docker.com/r/tgerczei/tea)
5. asdf (thirdparty): [mvaldes14/asdf-tea](https://github.com/mvaldes14/asdf-tea)
### Log in to Gitea from tea
Gitea can use many different authentication schemes, and not every authentication method will work with every Gitea deployment. When you are a Gitea instance administrator you can tweak your settings to fit your use case. For the method that is most likely to work with any Gitea deployment use the following steps:
1. Open your Gitea instance in a web browser
2. Log in to Gitea in your web browser. Any MFA, IDP, or whatever else should be available this way.
3. In your "user settings", generate an application token with at least **user read** permissions. If you want to do anything useful with the token add additional permissions/scopes.
4. Run `tea login add`, select **application token** authentication when asked for authentication type, and answer **yes** to the question if you have a token. Paste the generated token when asked for one.
You should now be logged in to your gitea instance from tea.
Since 0.10 Gitea supports the much simpler oauth workflow but oauth may not be available on all Gitea deployments, and gets much more complex when running tea on a remote system.
### Shell completion
If you installed from source or the package does not provide the completions with it you can add them yourself with `tea completion <shell>` command which is not visible in help. To generate the completions run one of the following commands depending on your shell.
```shell
# .bashrc
source <(tea completion bash)
# .zshrc
source <(tea completion zsh)
# fish
tea completion fish > ~/.config/fish/completions/tea.fish
# Powershell
Output the script to path/to/autocomplete/tea.ps1 an run it.
```
### Man Page
The hidden command `tea man` can be used to generate the `tea` man page.
```shell
# for bash or zsh
man <(tea man)
# for fish
man (tea man | psub)
# write man page to a file
tea man --out ./tea.man
```
## Compilation ## Compilation
Make sure you have a current go version installed (1.13 or newer). Make sure you have installed a current go version.
To compile the sources yourself run the following:
- To compile the source yourself with the recommended flags & tags:
```sh ```sh
git clone https://gitea.com/gitea/tea.git # or: tea clone gitea.com/gitea/tea ;) git clone https://gitea.com/gitea/tea.git
cd tea cd tea
make 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}
```
## Contributing ## Contributing
@ -187,6 +109,7 @@ Fork -> Patch -> Push -> Pull Request
- `make test` run testsuite - `make test` run testsuite
- `make vet` run checks (check the order of imports; preventing failure on CI pipeline beforehand) - `make vet` run checks (check the order of imports; preventing failure on CI pipeline beforehand)
- `make vendor` when adding new dependencies
- ... (for other development tasks, check the `Makefile`) - ... (for other development tasks, check the `Makefile`)
**Please** read the [CONTRIBUTING](CONTRIBUTING.md) documentation, it will tell you about internal structures and concepts. **Please** read the [CONTRIBUTING](CONTRIBUTING.md) documentation, it will tell you about internal structures and concepts.

View File

@ -1,7 +1,6 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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.
//go:build vendor
//+build vendor //+build vendor
package main package main

View File

@ -1,56 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
stdctx "context"
"code.gitea.io/tea/cmd/admin/users"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) error {
return cli.ShowSubcommandHelp(cmd)
},
Commands: []*cli.Command{
&cmdAdminUsers,
},
}
var cmdAdminUsers = cli.Command{
Name: "users",
Aliases: []string{"u"},
Usage: "Manage registered users",
Action: func(ctx stdctx.Context, cmd *cli.Command) error {
if cmd.Args().Len() == 1 {
return runAdminUserDetail(ctx, cmd, cmd.Args().First())
}
return users.RunUserList(ctx, cmd)
},
Commands: []*cli.Command{
&users.CmdUserList,
},
Flags: users.CmdUserList.Flags,
}
func runAdminUserDetail(_ stdctx.Context, cmd *cli.Command, 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,55 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package users
import (
stdctx "context"
"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/v3"
)
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(_ stdctx.Context, cmd *cli.Command) 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: flags.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/v3"
)
// 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,
Commands: []*cli.Command{
&attachments.CmdReleaseAttachmentList,
&attachments.CmdReleaseAttachmentCreate,
&attachments.CmdReleaseAttachmentDelete,
},
Flags: flags.AllDefaultFlags,
}

View File

@ -1,65 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package attachments
import (
stdctx "context"
"fmt"
"os"
"path/filepath"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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,101 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package attachments
import (
stdctx "context"
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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,75 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package attachments
import (
stdctx "context"
"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/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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: flags.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")
}

106
cmd/autocomplete.go Normal file
View File

@ -0,0 +1,106 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"fmt"
"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)",
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"
default:
return fmt.Errorf("Must specify valid shell type")
}
localPath, err := xdg.ConfigFile("tea/" + localFile)
if err != nil {
return err
}
cmds = fmt.Sprintf(cmds, localPath)
if err := saveAutoCompleteFile(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 saveAutoCompleteFile(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
}

View File

@ -1,38 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"code.gitea.io/tea/cmd/branches"
"github.com/urfave/cli/v3"
)
// 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,
Commands: []*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 context.Context, cmd *cli.Command) error {
return branches.RunBranchesList(ctx, cmd)
}

View File

@ -1,75 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package branches
import (
stdctx "context"
"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/v3"
)
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(_ stdctx.Context, cmd *cli.Command) 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: flags.GetListOptions(),
})
if err != nil {
return err
}
protections, _, err = ctx.Login.Client().ListBranchProtections(owner, ctx.Repo, gitea.ListBranchProtectionsOptions{
ListOptions: flags.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,102 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package branches
import (
stdctx "context"
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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,5 +1,6 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 package cmd
@ -7,5 +8,4 @@ var (
catSetup = "SETUP" catSetup = "SETUP"
catEntities = "ENTITIES" catEntities = "ENTITIES"
catHelpers = "HELPERS" catHelpers = "HELPERS"
catMisc = "MISCELLANEOUS"
) )

View File

@ -1,93 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
stdctx "context"
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/debug"
"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/v3"
)
// 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(ctx stdctx.Context, cmd *cli.Command) error {
teaCmd := context.InitCommand(cmd)
args := teaCmd.Args()
if args.Len() < 1 {
return cli.ShowCommandHelp(ctx, cmd, "clone")
}
dir := args.Get(1)
var (
login *config.Login = teaCmd.Login
owner string = teaCmd.Login.User
repo string
)
// parse first arg as repo specifier
repoSlug := args.Get(0)
url, err := git.ParseURL(repoSlug)
if err != nil {
return err
}
debug.Printf("Cloning repository %s into %s", url.String(), dir)
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)
}
debug.Printf("Matched login '%s' for host '%s'", login.Name, url.Host)
}
_, err = task.RepoClone(
dir,
login,
owner,
repo,
interact.PromptPassword,
teaCmd.Int("depth"),
)
return err
}

View File

@ -1,136 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Tea is command line tool for Gitea.
package cmd // import "code.gitea.io/tea"
import (
"fmt"
"runtime"
"strings"
"github.com/urfave/cli/v3"
)
// Version holds the current tea version
// If the Version is moved to another package or name changed,
// build flags in .goreleaser.yaml or Makefile need to be updated accordingly.
var Version = "development"
// Tags holds the build tags used
var Tags = ""
// SDK holds the sdk version from go.mod
var SDK = ""
// App creates and returns a tea Command with all subcommands set
// it was separated from main so docs can be generated for it
func App() *cli.Command {
// make parsing tea --version easier, by printing /just/ the version string
cli.VersionPrinter = func(c *cli.Command) { fmt.Fprintln(c.Writer, c.Version) }
return &cli.Command{
Name: "tea",
Usage: "command line tool to interact with Gitea",
Description: appDescription,
CustomHelpTemplate: helpTemplate,
Version: formatVersion(),
Commands: []*cli.Command{
&CmdLogin,
&CmdLogout,
&CmdWhoami,
&CmdIssues,
&CmdPulls,
&CmdLabels,
&CmdMilestones,
&CmdReleases,
&CmdTrackedTimes,
&CmdOrgs,
&CmdRepos,
&CmdBranches,
&CmdAddComment,
&CmdOpen,
&CmdNotifications,
&CmdRepoClone,
&CmdAdmin,
&CmdGenerateManPage,
},
EnableShellCompletion: true,
}
}
func formatVersion() string {
version := fmt.Sprintf("Version: %s\tgolang: %s",
bold(Version),
strings.ReplaceAll(runtime.Version(), "go", ""))
if len(Tags) != 0 {
version += fmt.Sprintf("\tbuilt with: %s", strings.Replace(Tags, " ", ", ", -1))
}
if len(SDK) != 0 {
version += fmt.Sprintf("\tgo-sdk: %s", SDK)
}
return version
}
var appDescription = `tea is a productivity helper for Gitea. It can be used to manage most entities on
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.
tea tries to make use of context provided by the repository in $PWD if available.
tea works best in a upstream/fork workflow, when the local main branch tracks the
upstream repo. tea assumes that local git state is published on the remote before
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.
`
var helpTemplate = bold(`
{{.Name}}{{if .Usage}} - {{.Usage}}{{end}}`) + `
{{if .Version}}{{if not .HideVersion}}version {{.Version}}{{end}}{{end}}
USAGE
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .Commands}} command [subcommand] [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}}
DESCRIPTION
{{.Description | nindent 3 | trim}}{{end}}{{if .VisibleCommands}}
COMMANDS{{range .VisibleCategories}}{{if .Name}}
{{.Name}}:{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}}
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
OPTIONS
{{range $index, $option := .VisibleFlags}}{{if $index}}
{{end}}{{$option}}{{end}}{{end}}
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.
`
func bold(t string) string {
return fmt.Sprintf("\033[1m%s\033[0m", t)
}

View File

@ -1,26 +1,22 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 package cmd
import ( import (
stdctx "context"
"errors"
"fmt" "fmt"
"io" "io/ioutil"
"strings" "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/interact"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/theme"
"code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/charmbracelet/huh" "code.gitea.io/tea/cmd/flags"
"github.com/urfave/cli/v3" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v2"
) )
// CmdAddComment is the main command to operate with notifications // CmdAddComment is the main command to operate with notifications
@ -35,7 +31,7 @@ var CmdAddComment = cli.Command{
Flags: flags.AllDefaultFlags, Flags: flags.AllDefaultFlags,
} }
func runAddComment(_ stdctx.Context, cmd *cli.Command) error { func runAddComment(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
@ -52,28 +48,19 @@ func runAddComment(_ stdctx.Context, cmd *cli.Command) error {
body := strings.Join(ctx.Args().Tail(), " ") body := strings.Join(ctx.Args().Tail(), " ")
if interact.IsStdinPiped() { if interact.IsStdinPiped() {
// custom solution until https://github.com/AlecAivazis/survey/issues/328 is fixed // custom solution until https://github.com/AlecAivazis/survey/issues/328 is fixed
if bodyStdin, err := io.ReadAll(ctx.Reader); err != nil { if bodyStdin, err := ioutil.ReadAll(ctx.App.Reader); err != nil {
return err return err
} else if len(bodyStdin) != 0 { } else if len(bodyStdin) != 0 {
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n") body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
} }
} else if len(body) == 0 { } else if len(body) == 0 {
if err := huh.NewForm( if body, err = interact.PromptMultiline("Content"); err != nil {
huh.NewGroup(
huh.NewText().
Title("Comment(markdown):").
ExternalEditor(config.GetPreferences().Editor).
EditorExtension("md").
Value(&body),
),
).WithTheme(theme.GetTheme()).
Run(); err != nil {
return err return err
} }
} }
if len(body) == 0 { if len(body) == 0 {
return errors.New("no comment content provided") return fmt.Errorf("No comment body provided")
} }
client := ctx.Login.Client() client := ctx.Login.Client()

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/v3"
)
// 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(cmd *cli.Command) ([]string, error) {
val := cmd.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
}

205
cmd/flags/flags.go Normal file
View File

@ -0,0 +1,205 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package flags
import (
"fmt"
"strings"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils"
"github.com/araddon/dateparse"
"github.com/urfave/cli/v2"
)
// LoginFlag provides flag to specify tea login profile
var LoginFlag = cli.StringFlag{
Name: "login",
Aliases: []string{"l"},
Usage: "Use a different Gitea Login. Optional",
}
// RepoFlag provides flag to specify repository
var RepoFlag = cli.StringFlag{
Name: "repo",
Aliases: []string{"r"},
Usage: "Override local repository path or gitea repository slug to interact with. Optional",
}
// RemoteFlag provides flag to specify remote repository
var RemoteFlag = cli.StringFlag{
Name: "remote",
Aliases: []string{"R"},
Usage: "Discover Gitea login from remote. Optional",
}
// OutputFlag provides flag to specify output type
var OutputFlag = cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "Output format. (csv, simple, table, tsv, yaml)",
}
// StateFlag provides flag to specify issue/pr state, defaulting to "open"
var StateFlag = cli.StringFlag{
Name: "state",
Usage: "Filter by state (all|open|closed)",
DefaultText: "open",
}
// PaginationPageFlag provides flag for pagination options
var PaginationPageFlag = cli.StringFlag{
Name: "page",
Aliases: []string{"p"},
Usage: "specify page, default is 1",
}
// PaginationLimitFlag provides flag for pagination options
var PaginationLimitFlag = cli.StringFlag{
Name: "limit",
Aliases: []string{"lm"},
Usage: "specify limit of items per page",
}
// LoginOutputFlags defines login and output flags that should
// added to all subcommands and appended to the flags of the
// subcommand to work around issue and provide --login and --output:
// https://github.com/urfave/cli/issues/585
var LoginOutputFlags = []cli.Flag{
&LoginFlag,
&OutputFlag,
}
// LoginRepoFlags defines login and repo flags that should
// be used for all subcommands and appended to the flags of
// the subcommand to work around issue and provide --login and --repo:
// https://github.com/urfave/cli/issues/585
var LoginRepoFlags = []cli.Flag{
&LoginFlag,
&RepoFlag,
&RemoteFlag,
}
// AllDefaultFlags defines flags that should be available
// for all subcommands working with dedicated repositories
// to work around issue and provide --login, --repo and --output:
// https://github.com/urfave/cli/issues/585
var AllDefaultFlags = append([]cli.Flag{
&RepoFlag,
&RemoteFlag,
}, LoginOutputFlags...)
// IssuePRFlags defines flags that should be available on issue & pr listing flags.
var IssuePRFlags = append([]cli.Flag{
&StateFlag,
&PaginationPageFlag,
&PaginationLimitFlag,
}, AllDefaultFlags...)
// IssuePREditFlags defines flags for properties of issues and PRs
var IssuePREditFlags = append([]cli.Flag{
&cli.StringFlag{
Name: "title",
Aliases: []string{"t"},
},
&cli.StringFlag{
Name: "description",
Aliases: []string{"d"},
},
&cli.StringFlag{
Name: "assignees",
Aliases: []string{"a"},
Usage: "Comma-separated list of usernames to assign",
},
&cli.StringFlag{
Name: "labels",
Aliases: []string{"L"},
Usage: "Comma-separated list of labels to assign",
},
&cli.StringFlag{
Name: "deadline",
Aliases: []string{"D"},
Usage: "Deadline timestamp to assign",
},
&cli.StringFlag{
Name: "milestone",
Aliases: []string{"m"},
Usage: "Milestone to assign",
},
}, LoginRepoFlags...)
// GetIssuePREditFlags parses all IssuePREditFlags
func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
opts := gitea.CreateIssueOption{
Title: ctx.String("title"),
Body: ctx.String("body"),
Assignees: strings.Split(ctx.String("assignees"), ","),
}
var err error
date := ctx.String("deadline")
if date != "" {
t, err := dateparse.ParseAny(date)
if err != nil {
return nil, err
}
opts.Deadline = &t
}
client := ctx.Login.Client()
labelNames := strings.Split(ctx.String("labels"), ",")
if len(labelNames) != 0 {
if client == nil {
client = ctx.Login.Client()
}
if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil {
return nil, err
}
}
if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 {
if client == nil {
client = ctx.Login.Client()
}
ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName)
if err != nil {
return nil, fmt.Errorf("Milestone '%s' not found", milestoneName)
}
opts.Milestone = ms.ID
}
return &opts, nil
}
// FieldsFlag generates a flag selecting printable fields.
// To retrieve the value, use GetFields()
func FieldsFlag(availableFields, defaultFields []string) *cli.StringFlag {
return &cli.StringFlag{
Name: "fields",
Aliases: []string{"f"},
Usage: fmt.Sprintf(`Comma-separated list of fields to print. Available values:
%s
`, strings.Join(availableFields, ",")),
Value: strings.Join(defaultFields, ","),
}
}
// GetFields parses the values provided in a fields flag, and
// optionally validates against valid values.
func GetFields(ctx *cli.Context, validFields []string) ([]string, error) {
selection := strings.Split(ctx.String("fields"), ",")
if validFields != nil {
for _, field := range selection {
if !utils.Contains(validFields, field) {
return nil, fmt.Errorf("Invalid field '%s'", field)
}
}
}
return selection, nil
}

View File

@ -1,143 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package flags
import (
"errors"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3"
)
// 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)",
}
var (
paging gitea.ListOptions
// ErrPage indicates that the provided page value is invalid (less than -1 or equal to 0).
ErrPage = errors.New("page cannot be smaller than 1")
// ErrLimit indicates that the provided limit value is invalid (negative).
ErrLimit = errors.New("limit cannot be negative")
)
// GetListOptions returns configured paging struct
func GetListOptions() gitea.ListOptions {
return paging
}
// PaginationFlags provides all pagination related flags
var PaginationFlags = []cli.Flag{
&PaginationPageFlag,
&PaginationLimitFlag,
}
// PaginationPageFlag provides flag for pagination options
var PaginationPageFlag = cli.IntFlag{
Name: "page",
Aliases: []string{"p"},
Usage: "specify page",
Value: 1,
Validator: func(i int) error {
if i < 1 && i != -1 {
return ErrPage
}
return nil
},
Destination: &paging.Page,
}
// PaginationLimitFlag provides flag for pagination options
var PaginationLimitFlag = cli.IntFlag{
Name: "limit",
Aliases: []string{"lm"},
Usage: "specify limit of items per page",
Value: 30,
Validator: func(i int) error {
if i < 0 {
return ErrLimit
}
return nil
},
Destination: &paging.PageSize,
}
// 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,125 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package flags
import (
"context"
"io"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v3"
)
func TestPaginationFlags(t *testing.T) {
var (
defaultPage = PaginationPageFlag.Value
defaultLimit = PaginationLimitFlag.Value
)
cases := []struct {
name string
args []string
expectedPage int
expectedLimit int
}{
{
name: "no flags",
args: []string{"test"},
expectedPage: defaultPage,
expectedLimit: defaultLimit,
},
{
name: "only paging",
args: []string{"test", "--page", "5"},
expectedPage: 5,
expectedLimit: defaultLimit,
},
{
name: "only limit",
args: []string{"test", "--limit", "10"},
expectedPage: defaultPage,
expectedLimit: 10,
},
{
name: "only limit",
args: []string{"test", "--limit", "10"},
expectedPage: defaultPage,
expectedLimit: 10,
},
{
name: "both flags",
args: []string{"test", "--page", "2", "--limit", "20"},
expectedPage: 2,
expectedLimit: 20,
},
{ //TODO: Should no paging be applied as -1 or a separate flag? It's not obvious that page=-1 turns off paging and limit is ignored
name: "no paging",
args: []string{"test", "--limit", "20", "--page", "-1"},
expectedPage: -1,
expectedLimit: 20,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
cmd := cli.Command{
Name: "test-paging",
Action: func(_ context.Context, cmd *cli.Command) error {
assert.Equal(t, tc.expectedPage, cmd.Int("page"))
assert.Equal(t, tc.expectedLimit, cmd.Int("limit"))
return nil
},
Flags: PaginationFlags,
}
err := cmd.Run(context.Background(), tc.args)
require.NoError(t, err)
})
}
}
func TestPaginationFailures(t *testing.T) {
cases := []struct {
name string
args []string
expectedError error
}{
{
name: "negative limit",
args: []string{"test", "--limit", "-10"},
expectedError: ErrLimit,
},
{
name: "negative paging",
args: []string{"test", "--page", "-2"},
expectedError: ErrPage,
},
{
name: "zero paging",
args: []string{"test", "--page", "0"},
expectedError: ErrPage,
},
{
//urfave does not validate all flags in one pass
name: "negative paging and paging",
args: []string{"test", "--page", "-2", "--limit", "-10"},
expectedError: ErrPage,
},
}
for _, tc := range cases {
cmd := cli.Command{
Name: "test-paging",
Flags: PaginationFlags,
Writer: io.Discard,
ErrWriter: io.Discard,
}
t.Run(tc.name, func(t *testing.T) {
err := cmd.Run(context.Background(), tc.args)
require.ErrorContains(t, err, tc.expectedError.Error())
// require.ErrorIs(t, err, tc.expectedError)
})
}
}

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/v3"
)
// 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,10 +1,10 @@
// Copyright 2018 The Gitea Authors. All rights reserved. // 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 package cmd
import ( import (
stdctx "context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/issues" "code.gitea.io/tea/cmd/issues"
@ -13,7 +13,7 @@ import (
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdIssues represents to login a gitea server. // CmdIssues represents to login a gitea server.
@ -25,29 +25,28 @@ var CmdIssues = cli.Command{
Description: `Lists issues when called without argument. If issue index is provided, will show it in detail.`, Description: `Lists issues when called without argument. If issue index is provided, will show it in detail.`,
ArgsUsage: "[<issue index>]", ArgsUsage: "[<issue index>]",
Action: runIssues, Action: runIssues,
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&issues.CmdIssuesList, &issues.CmdIssuesList,
&issues.CmdIssuesCreate, &issues.CmdIssuesCreate,
&issues.CmdIssuesEdit,
&issues.CmdIssuesReopen, &issues.CmdIssuesReopen,
&issues.CmdIssuesClose, &issues.CmdIssuesClose,
}, },
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "comments", Name: "comments",
Usage: "Whether to display comments (will prompt if not provided & run interactively)", Usage: "Wether to display comments (will prompt if not provided & run interactively)",
}, },
}, issues.CmdIssuesList.Flags...), }, issues.CmdIssuesList.Flags...),
} }
func runIssues(ctx stdctx.Context, cmd *cli.Command) error { func runIssues(ctx *cli.Context) error {
if cmd.Args().Len() == 1 { if ctx.Args().Len() == 1 {
return runIssueDetail(ctx, cmd, cmd.Args().First()) return runIssueDetail(ctx, ctx.Args().First())
} }
return issues.RunIssuesList(ctx, cmd) return issues.RunIssuesList(ctx)
} }
func runIssueDetail(_ stdctx.Context, cmd *cli.Command, index string) error { func runIssueDetail(cmd *cli.Context, index string) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
@ -55,16 +54,11 @@ func runIssueDetail(_ stdctx.Context, cmd *cli.Command, index string) error {
if err != nil { if err != nil {
return err return err
} }
client := ctx.Login.Client() issue, _, err := ctx.Login.Client().GetIssue(ctx.Owner, ctx.Repo, idx)
issue, _, err := client.GetIssue(ctx.Owner, ctx.Repo, idx)
if err != nil { if err != nil {
return err return err
} }
reactions, _, err := client.GetIssueReactions(ctx.Owner, ctx.Repo, idx) print.IssueDetails(issue)
if err != nil {
return err
}
print.IssueDetails(issue, reactions)
if issue.Comments > 0 { if issue.Comments > 0 {
err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments) err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments)

View File

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

View File

@ -1,17 +1,16 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 issues package issues
import ( import (
stdctx "context"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdIssuesCreate represents a sub command of issues to create issue // CmdIssuesCreate represents a sub command of issues to create issue
@ -20,24 +19,19 @@ var CmdIssuesCreate = cli.Command{
Aliases: []string{"c"}, Aliases: []string{"c"},
Usage: "Create an issue on repository", Usage: "Create an issue on repository",
Description: `Create an issue on repository`, Description: `Create an issue on repository`,
ArgsUsage: " ", // command does not accept arguments
Action: runIssuesCreate, Action: runIssuesCreate,
Flags: flags.IssuePRCreateFlags, Flags: flags.IssuePREditFlags,
} }
func runIssuesCreate(_ stdctx.Context, cmd *cli.Command) error { func runIssuesCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.NumFlags() == 0 { if ctx.NumFlags() == 0 {
err := interact.CreateIssue(ctx.Login, ctx.Owner, ctx.Repo) return interact.CreateIssue(ctx.Login, ctx.Owner, ctx.Repo)
if err != nil && !interact.IsQuitting(err) {
return err
}
return nil
} }
opts, err := flags.GetIssuePRCreateFlags(ctx) opts, err := flags.GetIssuePREditFlags(ctx)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,75 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
import (
"fmt"
stdctx "context"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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 {
if ctx.NumFlags() == 0 {
var err error
opts, err = interact.EditIssue(*ctx, opts.Index)
if err != nil {
if interact.IsQuitting(err) {
return nil // user quit
}
return err
}
}
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,128 +1,58 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 issues package issues
import ( import (
stdctx "context"
"fmt"
"time"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/araddon/dateparse" "github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
) )
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 // CmdIssuesList represents a sub command of issues to list issues
var CmdIssuesList = cli.Command{ var CmdIssuesList = cli.Command{
Name: "list", Name: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Usage: "List issues of the repository", Usage: "List issues of the repository",
Description: `List issues of the repository`, Description: `List issues of the repository`,
ArgsUsage: " ", // command does not accept arguments
Action: RunIssuesList, Action: RunIssuesList,
Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssueListingFlags...), Flags: append([]cli.Flag{
flags.FieldsFlag(print.IssueFields, []string{
"index", "title", "state", "author", "milestone", "labels",
}),
}, flags.IssuePRFlags...),
} }
// RunIssuesList list issues // RunIssuesList list issues
func RunIssuesList(_ stdctx.Context, cmd *cli.Command) error { func RunIssuesList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
state := gitea.StateOpen state := gitea.StateOpen
switch ctx.String("state") { switch ctx.String("state") {
case "all": case "all":
state = gitea.StateAll state = gitea.StateAll
case "", "open": case "open":
state = gitea.StateOpen state = gitea.StateOpen
case "closed": case "closed":
state = gitea.StateClosed state = gitea.StateClosed
default:
return fmt.Errorf("unknown state '%s'", ctx.String("state"))
} }
kind := gitea.IssueTypeIssue issues, _, err := ctx.Login.Client().ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{
switch ctx.String("kind") { ListOptions: ctx.GetListOptions(),
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: flags.GetListOptions(),
State: state, State: state,
Type: kind, Type: gitea.IssueTypeIssue,
KeyWord: ctx.String("keyword"),
CreatedBy: ctx.String("author"),
AssignedBy: ctx.String("assigned-to"),
MentionedBy: ctx.String("mentions"),
Labels: labels,
Milestones: milestones,
Since: from,
Before: until,
}) })
if err != nil { if err != nil {
return err return err
} }
} else {
issues, _, err = ctx.Login.Client().ListIssues(gitea.ListIssueOption{
ListOptions: flags.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 { fields, err := flags.GetFields(cmd, print.IssueFields)
return err
}
}
fields, err := issueFieldsFlag.GetValues(cmd)
if err != nil { if err != nil {
return err return err
} }

View File

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

View File

@ -1,14 +1,14 @@
// Copyright 2019 The Gitea Authors. All rights reserved. // 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 package cmd
import ( import (
"context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/labels" "code.gitea.io/tea/cmd/labels"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLabels represents to operate repositories' labels. // CmdLabels represents to operate repositories' labels.
@ -18,24 +18,22 @@ var CmdLabels = cli.Command{
Category: catEntities, Category: catEntities,
Usage: "Manage issue labels", Usage: "Manage issue labels",
Description: `Manage issue labels`, Description: `Manage issue labels`,
ArgsUsage: " ", // command does not accept arguments
Action: runLabels, Action: runLabels,
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&labels.CmdLabelsList, &labels.CmdLabelsList,
&labels.CmdLabelCreate, &labels.CmdLabelCreate,
&labels.CmdLabelUpdate, &labels.CmdLabelUpdate,
&labels.CmdLabelDelete, &labels.CmdLabelDelete,
}, },
Flags: labels.CmdLabelsList.Flags,
} }
func runLabels(ctx context.Context, cmd *cli.Command) error { func runLabels(ctx *cli.Context) error {
if cmd.Args().Len() == 1 { if ctx.Args().Len() == 1 {
return runLabelsDetails(cmd) return runLabelsDetails(ctx)
} }
return labels.RunLabelsList(ctx, cmd) return labels.RunLabelsList(ctx)
} }
func runLabelsDetails(cmd *cli.Command) error { func runLabelsDetails(ctx *cli.Context) error {
return fmt.Errorf("Not yet implemented") return fmt.Errorf("Not yet implemented")
} }

View File

@ -1,20 +1,19 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 labels
import ( import (
"bufio" "bufio"
stdctx "context"
"log" "log"
"os" "os"
"strings" "strings"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLabelCreate represents a sub command of labels to create label. // CmdLabelCreate represents a sub command of labels to create label.
@ -23,9 +22,8 @@ var CmdLabelCreate = cli.Command{
Aliases: []string{"c"}, Aliases: []string{"c"},
Usage: "Create a label", Usage: "Create a label",
Description: `Create a label`, Description: `Create a label`,
ArgsUsage: " ", // command does not accept arguments
Action: runLabelCreate, Action: runLabelCreate,
Flags: append([]cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Usage: "label name", Usage: "label name",
@ -42,10 +40,10 @@ var CmdLabelCreate = cli.Command{
Name: "file", Name: "file",
Usage: "indicate a label file", Usage: "indicate a label file",
}, },
}, flags.AllDefaultFlags...), },
} }
func runLabelCreate(_ stdctx.Context, cmd *cli.Command) error { func runLabelCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})

View File

@ -1,5 +1,6 @@
// Copyright 2019 The Gitea Authors. All rights reserved. // 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 labels

View File

@ -1,15 +1,13 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 labels
import ( import (
stdctx "context"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLabelDelete represents a sub command of labels to delete label. // CmdLabelDelete represents a sub command of labels to delete label.
@ -18,17 +16,16 @@ var CmdLabelDelete = cli.Command{
Aliases: []string{"rm"}, Aliases: []string{"rm"},
Usage: "Delete a label", Usage: "Delete a label",
Description: `Delete a label`, Description: `Delete a label`,
ArgsUsage: " ", // command does not accept arguments
Action: runLabelDelete, Action: runLabelDelete,
Flags: append([]cli.Flag{ Flags: []cli.Flag{
&cli.IntFlag{ &cli.IntFlag{
Name: "id", Name: "id",
Usage: "label id", Usage: "label id",
}, },
}, flags.AllDefaultFlags...), },
} }
func runLabelDelete(_ stdctx.Context, cmd *cli.Command) error { func runLabelDelete(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})

View File

@ -1,18 +1,17 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 labels
import ( import (
stdctx "context"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLabelsList represents a sub command of labels to list labels // CmdLabelsList represents a sub command of labels to list labels
@ -21,7 +20,6 @@ var CmdLabelsList = cli.Command{
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Usage: "List labels", Usage: "List labels",
Description: "List labels", Description: "List labels",
ArgsUsage: " ", // command does not accept arguments
Action: RunLabelsList, Action: RunLabelsList,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
@ -35,13 +33,13 @@ var CmdLabelsList = cli.Command{
} }
// RunLabelsList list labels. // RunLabelsList list labels.
func RunLabelsList(_ stdctx.Context, cmd *cli.Command) error { func RunLabelsList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client() client := ctx.Login.Client()
labels, _, err := client.ListRepoLabels(ctx.Owner, ctx.Repo, gitea.ListLabelsOptions{ labels, _, err := client.ListRepoLabels(ctx.Owner, ctx.Repo, gitea.ListLabelsOptions{
ListOptions: flags.GetListOptions(), ListOptions: ctx.GetListOptions(),
}) })
if err != nil { if err != nil {
return err return err

View File

@ -1,16 +1,14 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 labels
import ( import (
stdctx "context"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLabelUpdate represents a sub command of labels to update label. // CmdLabelUpdate represents a sub command of labels to update label.
@ -18,9 +16,8 @@ var CmdLabelUpdate = cli.Command{
Name: "update", Name: "update",
Usage: "Update a label", Usage: "Update a label",
Description: `Update a label`, Description: `Update a label`,
ArgsUsage: " ", // command does not accept arguments
Action: runLabelUpdate, Action: runLabelUpdate,
Flags: append([]cli.Flag{ Flags: []cli.Flag{
&cli.IntFlag{ &cli.IntFlag{
Name: "id", Name: "id",
Usage: "label id", Usage: "label id",
@ -37,10 +34,10 @@ var CmdLabelUpdate = cli.Command{
Name: "description", Name: "description",
Usage: "label description", Usage: "label description",
}, },
}, flags.AllDefaultFlags...), },
} }
func runLabelUpdate(_ stdctx.Context, cmd *cli.Command) error { func runLabelUpdate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})

View File

@ -1,17 +1,17 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 package cmd
import ( import (
"context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/login" "code.gitea.io/tea/cmd/login"
"code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLogin represents to login a gitea server. // CmdLogin represents to login a gitea server.
@ -23,22 +23,20 @@ var CmdLogin = cli.Command{
Description: `Log in to a Gitea server`, Description: `Log in to a Gitea server`,
ArgsUsage: "[<login name>]", ArgsUsage: "[<login name>]",
Action: runLogins, Action: runLogins,
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&login.CmdLoginList, &login.CmdLoginList,
&login.CmdLoginAdd, &login.CmdLoginAdd,
&login.CmdLoginEdit, &login.CmdLoginEdit,
&login.CmdLoginDelete, &login.CmdLoginDelete,
&login.CmdLoginSetDefault, &login.CmdLoginSetDefault,
&login.CmdLoginHelper,
&login.CmdLoginOAuthRefresh,
}, },
} }
func runLogins(ctx context.Context, cmd *cli.Command) error { func runLogins(ctx *cli.Context) error {
if cmd.Args().Len() == 1 { if ctx.Args().Len() == 1 {
return runLoginDetail(cmd.Args().First()) return runLoginDetail(ctx.Args().First())
} }
return login.RunLoginList(ctx, cmd) return login.RunLoginList(ctx)
} }
func runLoginDetail(name string) error { func runLoginDetail(name string) error {

View File

@ -1,17 +1,14 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 login package login
import ( import (
"context"
"fmt"
"code.gitea.io/tea/modules/auth"
"code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLoginAdd represents to login a gitea server. // CmdLoginAdd represents to login a gitea server.
@ -19,7 +16,6 @@ var CmdLoginAdd = cli.Command{
Name: "add", Name: "add",
Usage: "Add a Gitea login", Usage: "Add a Gitea login",
Description: `Add a Gitea login, without args it will create one interactively`, Description: `Add a Gitea login, without args it will create one interactively`,
ArgsUsage: " ", // command does not accept arguments
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
@ -30,136 +26,56 @@ var CmdLoginAdd = cli.Command{
Name: "url", Name: "url",
Aliases: []string{"u"}, Aliases: []string{"u"},
Value: "https://gitea.com", Value: "https://gitea.com",
Sources: cli.EnvVars("GITEA_SERVER_URL"), EnvVars: []string{"GITEA_SERVER_URL"},
Usage: "Server URL", Usage: "Server URL",
}, },
&cli.BoolFlag{
Name: "no-version-check",
Aliases: []string{"nv"},
Usage: "Do not check version of Gitea instance",
},
&cli.StringFlag{ &cli.StringFlag{
Name: "token", Name: "token",
Aliases: []string{"t"}, Aliases: []string{"t"},
Value: "", Value: "",
Sources: cli.EnvVars("GITEA_SERVER_TOKEN"), EnvVars: []string{"GITEA_SERVER_TOKEN"},
Usage: "Access token. Can be obtained from Settings > Applications", Usage: "Access token. Can be obtained from Settings > Applications",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "user", Name: "user",
Value: "", Value: "",
Sources: cli.EnvVars("GITEA_SERVER_USER"), EnvVars: []string{"GITEA_SERVER_USER"},
Usage: "User for basic auth (will create token)", Usage: "User for basic auth (will create token)",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "password", Name: "password",
Aliases: []string{"pwd"}, Aliases: []string{"pwd"},
Value: "", Value: "",
Sources: cli.EnvVars("GITEA_SERVER_PASSWORD"), EnvVars: []string{"GITEA_SERVER_PASSWORD"},
Usage: "Password for basic auth (will create token)", Usage: "Password for basic auth (will create token)",
}, },
&cli.StringFlag{
Name: "otp",
Sources: cli.EnvVars("GITEA_SERVER_OTP"),
Usage: "OTP token for auth, if necessary",
},
&cli.StringFlag{
Name: "scopes",
Sources: cli.EnvVars("GITEA_SCOPES"),
Usage: "Token scopes to add when creating a new token, separated by a comma",
},
&cli.StringFlag{ &cli.StringFlag{
Name: "ssh-key", Name: "ssh-key",
Aliases: []string{"s"}, Aliases: []string{"s"},
Usage: "Path to a SSH key/certificate to use, overrides auto-discovery", Usage: "Path to a SSH key to use, overrides auto-discovery",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "insecure", Name: "insecure",
Aliases: []string{"i"}, Aliases: []string{"i"},
Usage: "Disable TLS verification", 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",
},
&cli.BoolFlag{
Name: "oauth",
Aliases: []string{"o"},
Usage: "Use interactive OAuth2 flow for authentication",
},
&cli.StringFlag{
Name: "client-id",
Usage: "OAuth client ID (for use with --oauth)",
},
&cli.StringFlag{
Name: "redirect-url",
Usage: "OAuth redirect URL (for use with --oauth)",
},
}, },
Action: runLoginAdd, Action: runLoginAdd,
} }
func runLoginAdd(_ context.Context, cmd *cli.Command) error { func runLoginAdd(ctx *cli.Context) error {
// if no args create login interactive // if no args create login interactive
if cmd.NumFlags() == 0 { if ctx.NumFlags() == 0 {
if err := interact.CreateLogin(); err != nil && !interact.IsQuitting(err) { return interact.CreateLogin()
return fmt.Errorf("error adding login: %w", err)
}
return nil
}
// if OAuth flag is provided, use OAuth2 PKCE flow
if cmd.Bool("oauth") {
opts := auth.OAuthOptions{
Name: cmd.String("name"),
URL: cmd.String("url"),
Insecure: cmd.Bool("insecure"),
}
// Only set clientID if provided
if cmd.String("client-id") != "" {
opts.ClientID = cmd.String("client-id")
}
// Only set redirect URL if provided
if cmd.String("redirect-url") != "" {
opts.RedirectURL = cmd.String("redirect-url")
}
return auth.OAuthLoginWithFullOptions(opts)
}
sshAgent := false
if cmd.String("ssh-agent-key") != "" || cmd.String("ssh-agent-principal") != "" {
sshAgent = true
} }
// else use args to add login // else use args to add login
return task.CreateLogin( return task.CreateLogin(
cmd.String("name"), ctx.String("name"),
cmd.String("token"), ctx.String("token"),
cmd.String("user"), ctx.String("user"),
cmd.String("password"), ctx.String("password"),
cmd.String("otp"), ctx.String("ssh-key"),
cmd.String("scopes"), ctx.String("url"),
cmd.String("ssh-key"), ctx.Bool("insecure"))
cmd.String("url"),
cmd.String("ssh-agent-principal"),
cmd.String("ssh-agent-key"),
cmd.Bool("insecure"),
sshAgent,
!cmd.Bool("no-version-check"),
cmd.Bool("helper"),
)
} }

View File

@ -1,16 +1,16 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 login package login
import ( import (
"context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/config"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLoginSetDefault represents to login a gitea server. // CmdLoginSetDefault represents to login a gitea server.
@ -23,8 +23,8 @@ var CmdLoginSetDefault = cli.Command{
Flags: []cli.Flag{&flags.OutputFlag}, Flags: []cli.Flag{&flags.OutputFlag},
} }
func runLoginSetDefault(_ context.Context, cmd *cli.Command) error { func runLoginSetDefault(ctx *cli.Context) error {
if cmd.Args().Len() == 0 { if ctx.Args().Len() == 0 {
l, err := config.GetDefaultLogin() l, err := config.GetDefaultLogin()
if err != nil { if err != nil {
return err return err
@ -33,6 +33,6 @@ func runLoginSetDefault(_ context.Context, cmd *cli.Command) error {
return nil return nil
} }
name := cmd.Args().First() name := ctx.Args().First()
return config.SetDefaultLogin(name) return config.SetDefaultLogin(name)
} }

View File

@ -1,16 +1,16 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 login package login
import ( import (
"context"
"errors" "errors"
"log" "log"
"code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/config"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLoginDelete is a command to delete a login // CmdLoginDelete is a command to delete a login
@ -24,7 +24,7 @@ var CmdLoginDelete = cli.Command{
} }
// RunLoginDelete runs the action of a login delete command // RunLoginDelete runs the action of a login delete command
func RunLoginDelete(_ context.Context, cmd *cli.Command) error { func RunLoginDelete(ctx *cli.Context) error {
logins, err := config.GetLogins() logins, err := config.GetLogins()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -32,8 +32,8 @@ func RunLoginDelete(_ context.Context, cmd *cli.Command) error {
var name string var name string
if len(cmd.Args().First()) != 0 { if len(ctx.Args().First()) != 0 {
name = cmd.Args().First() name = ctx.Args().First()
} else if len(logins) == 1 { } else if len(logins) == 1 {
name = logins[0].Name name = logins[0].Name
} else { } else {

View File

@ -1,19 +1,15 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 login package login
import ( import (
"context"
"log"
"os"
"os/exec"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/config"
"github.com/skratchdot/open-golang/open" "github.com/skratchdot/open-golang/open"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLoginEdit represents to login a gitea server. // CmdLoginEdit represents to login a gitea server.
@ -22,20 +18,10 @@ var CmdLoginEdit = cli.Command{
Aliases: []string{"e"}, Aliases: []string{"e"},
Usage: "Edit Gitea logins", Usage: "Edit Gitea logins",
Description: `Edit Gitea logins`, Description: `Edit Gitea logins`,
ArgsUsage: " ", // command does not accept arguments
Action: runLoginEdit, Action: runLoginEdit,
Flags: []cli.Flag{&flags.OutputFlag}, Flags: []cli.Flag{&flags.OutputFlag},
} }
func runLoginEdit(_ context.Context, _ *cli.Command) error { func runLoginEdit(_ *cli.Context) error {
if e, ok := os.LookupEnv("EDITOR"); ok && e != "" {
cmd := exec.Command(e, config.GetConfigPath())
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err.Error())
}
}
return open.Start(config.GetConfigPath()) return open.Start(config.GetConfigPath())
} }

View File

@ -1,131 +0,0 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"bufio"
"context"
"fmt"
"log"
"net/url"
"os"
"strings"
"time"
"code.gitea.io/tea/modules/auth"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/task"
"github.com/urfave/cli/v3"
)
// 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,
Commands: []*cli.Command{
{
Name: "store",
Description: "Command drops",
Aliases: []string{"erase"},
Action: func(_ context.Context, _ *cli.Command) error {
return nil
},
},
{
Name: "setup",
Description: "Setup helper to tea authenticate",
Action: func(_ context.Context, _ *cli.Command) 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(_ context.Context, cmd *cli.Command) 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 userConfig == nil {
log.Fatal("host not exists")
} else if len(userConfig.Token) == 0 {
log.Fatal("User no set")
}
host, err := url.Parse(userConfig.URL)
if err != nil {
return err
}
if userConfig.TokenExpiry > 0 && time.Now().Unix() > userConfig.TokenExpiry {
// Token is expired, refresh it
err = auth.RefreshAccessToken(userConfig)
if err != nil {
return err
}
// Once token is refreshed, get the latest from the updated config
refreshedConfig := config.GetLoginByHost(wants["host"])
if refreshedConfig != nil {
userConfig = refreshedConfig
}
}
_, 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,16 +1,15 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 login package login
import ( import (
"context"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLoginList represents to login a gitea server. // CmdLoginList represents to login a gitea server.
@ -19,13 +18,12 @@ var CmdLoginList = cli.Command{
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Usage: "List Gitea logins", Usage: "List Gitea logins",
Description: `List Gitea logins`, Description: `List Gitea logins`,
ArgsUsage: " ", // command does not accept arguments
Action: RunLoginList, Action: RunLoginList,
Flags: []cli.Flag{&flags.OutputFlag}, Flags: []cli.Flag{&flags.OutputFlag},
} }
// RunLoginList list all logins // RunLoginList list all logins
func RunLoginList(_ context.Context, cmd *cli.Command) error { func RunLoginList(cmd *cli.Context) error {
logins, err := config.GetLogins() logins, err := config.GetLogins()
if err != nil { if err != nil {
return err return err

View File

@ -1,59 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package login
import (
"context"
"fmt"
"code.gitea.io/tea/modules/auth"
"code.gitea.io/tea/modules/config"
"github.com/urfave/cli/v3"
)
// CmdLoginOAuthRefresh represents a command to refresh an OAuth token
var CmdLoginOAuthRefresh = cli.Command{
Name: "oauth-refresh",
Usage: "Refresh an OAuth token",
Description: "Manually refresh an expired OAuth token. Usually only used when troubleshooting authentication.",
ArgsUsage: "[<login name>]",
Action: runLoginOAuthRefresh,
}
func runLoginOAuthRefresh(_ context.Context, cmd *cli.Command) error {
var loginName string
// Get login name from args or use default
if cmd.Args().Len() > 0 {
loginName = cmd.Args().First()
} else {
// Get default login
login, err := config.GetDefaultLogin()
if err != nil {
return fmt.Errorf("no login specified and no default login found: %s", err)
}
loginName = login.Name
}
// Get the login from config
login := config.GetLoginByName(loginName)
if login == nil {
return fmt.Errorf("login '%s' not found", loginName)
}
// Check if the login has a refresh token
if login.RefreshToken == "" {
return fmt.Errorf("login '%s' does not have a refresh token. It may have been created using a different authentication method", loginName)
}
// Refresh the token
err := auth.RefreshAccessToken(login)
if err != nil {
return fmt.Errorf("failed to refresh token: %s", err)
}
fmt.Printf("Successfully refreshed OAuth token for %s\n", loginName)
return nil
}

View File

@ -1,12 +1,13 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 package cmd
import ( import (
"code.gitea.io/tea/cmd/login" "code.gitea.io/tea/cmd/login"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdLogout represents to logout a gitea server. // CmdLogout represents to logout a gitea server.

View File

@ -1,62 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"fmt"
"os"
"path/filepath"
docs "github.com/urfave/cli-docs/v3"
"github.com/urfave/cli/v3"
)
// DocRenderFlags are the flags for documentation generation, used by `./docs/docs.go` and the `generate-man-page` sub command
var DocRenderFlags = []cli.Flag{
&cli.StringFlag{
Name: "out",
Usage: "Path to output docs to, otherwise prints to stdout",
Aliases: []string{"o"},
},
}
// CmdGenerateManPage is the sub command to generate the `tea` man page
var CmdGenerateManPage = cli.Command{
Name: "man",
Usage: "Generate man page",
Hidden: true,
Flags: DocRenderFlags,
Action: func(ctx context.Context, cmd *cli.Command) error {
return RenderDocs(cmd, cmd.Root(), docs.ToMan)
},
}
// RenderDocs renders the documentation for `target` using the supplied `render` function
func RenderDocs(cmd, target *cli.Command, render func(*cli.Command) (string, error)) error {
out, err := render(target)
if err != nil {
return err
}
outPath := cmd.String("out")
if outPath == "" {
fmt.Print(out)
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(out); err != nil {
return err
}
return nil
}

View File

@ -1,15 +1,15 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 package cmd
import ( import (
stdctx "context"
"code.gitea.io/tea/cmd/milestones" "code.gitea.io/tea/cmd/milestones"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3"
"github.com/urfave/cli/v2"
) )
// CmdMilestones represents to operate repositories milestones. // CmdMilestones represents to operate repositories milestones.
@ -21,7 +21,7 @@ var CmdMilestones = cli.Command{
Description: `List and create milestones`, Description: `List and create milestones`,
ArgsUsage: "[<milestone name>]", ArgsUsage: "[<milestone name>]",
Action: runMilestones, Action: runMilestones,
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&milestones.CmdMilestonesList, &milestones.CmdMilestonesList,
&milestones.CmdMilestonesCreate, &milestones.CmdMilestonesCreate,
&milestones.CmdMilestonesClose, &milestones.CmdMilestonesClose,
@ -32,14 +32,14 @@ var CmdMilestones = cli.Command{
Flags: milestones.CmdMilestonesList.Flags, Flags: milestones.CmdMilestonesList.Flags,
} }
func runMilestones(ctx stdctx.Context, cmd *cli.Command) error { func runMilestones(ctx *cli.Context) error {
if cmd.Args().Len() == 1 { if ctx.Args().Len() == 1 {
return runMilestoneDetail(ctx, cmd, cmd.Args().First()) return runMilestoneDetail(ctx, ctx.Args().First())
} }
return milestones.RunMilestonesList(ctx, cmd) return milestones.RunMilestonesList(ctx)
} }
func runMilestoneDetail(_ stdctx.Context, cmd *cli.Command, name string) error { func runMilestoneDetail(cmd *cli.Context, name string) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client() client := ctx.Login.Client()

View File

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

View File

@ -1,20 +1,20 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 milestones package milestones
import ( import (
"time" "time"
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"code.gitea.io/sdk/gitea"
"github.com/araddon/dateparse" "github.com/araddon/dateparse"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdMilestonesCreate represents a sub command of milestones to create milestone // CmdMilestonesCreate represents a sub command of milestones to create milestone
@ -23,7 +23,6 @@ var CmdMilestonesCreate = cli.Command{
Aliases: []string{"c"}, Aliases: []string{"c"},
Usage: "Create an milestone on repository", Usage: "Create an milestone on repository",
Description: `Create an milestone on repository`, Description: `Create an milestone on repository`,
ArgsUsage: " ", // command does not accept arguments
Action: runMilestonesCreate, Action: runMilestonesCreate,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
@ -49,14 +48,14 @@ var CmdMilestonesCreate = cli.Command{
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
func runMilestonesCreate(_ stdctx.Context, cmd *cli.Command) error { func runMilestonesCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
date := ctx.String("deadline") date := ctx.String("deadline")
deadline := &time.Time{} deadline := &time.Time{}
if date != "" { if date != "" {
t, err := dateparse.ParseAny(date) t, err := dateparse.ParseAny(date)
if err != nil { if err == nil {
return err return err
} }
deadline = &t deadline = &t
@ -68,10 +67,7 @@ func runMilestonesCreate(_ stdctx.Context, cmd *cli.Command) error {
} }
if ctx.NumFlags() == 0 { if ctx.NumFlags() == 0 {
if err := interact.CreateMilestone(ctx.Login, ctx.Owner, ctx.Repo); err != nil && !interact.IsQuitting(err) { return interact.CreateMilestone(ctx.Login, ctx.Owner, ctx.Repo)
return err
}
return nil
} }
return task.CreateMilestone( return task.CreateMilestone(

View File

@ -1,15 +1,14 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 milestones package milestones
import ( import (
stdctx "context"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdMilestonesDelete represents a sub command of milestones to delete an milestone // CmdMilestonesDelete represents a sub command of milestones to delete an milestone
@ -23,7 +22,7 @@ var CmdMilestonesDelete = cli.Command{
Flags: flags.AllDefaultFlags, Flags: flags.AllDefaultFlags,
} }
func deleteMilestone(_ stdctx.Context, cmd *cli.Command) error { func deleteMilestone(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client() client := ctx.Login.Client()

View File

@ -1,24 +1,20 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 milestones package milestones
import ( import (
"fmt" "fmt"
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v3"
)
var msIssuesFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{ "code.gitea.io/sdk/gitea"
"index", "kind", "title", "state", "updated", "labels", "github.com/urfave/cli/v2"
}) )
// CmdMilestonesIssues represents a sub command of milestones to manage issue/pull of an milestone // CmdMilestonesIssues represents a sub command of milestones to manage issue/pull of an milestone
var CmdMilestonesIssues = cli.Command{ var CmdMilestonesIssues = cli.Command{
@ -28,7 +24,7 @@ var CmdMilestonesIssues = cli.Command{
Description: "manage issue/pull of an milestone", Description: "manage issue/pull of an milestone",
ArgsUsage: "<milestone name>", ArgsUsage: "<milestone name>",
Action: runMilestoneIssueList, Action: runMilestoneIssueList,
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&CmdMilestoneAddIssue, &CmdMilestoneAddIssue,
&CmdMilestoneRemoveIssue, &CmdMilestoneRemoveIssue,
}, },
@ -44,7 +40,9 @@ var CmdMilestonesIssues = cli.Command{
}, },
&flags.PaginationPageFlag, &flags.PaginationPageFlag,
&flags.PaginationLimitFlag, &flags.PaginationLimitFlag,
msIssuesFieldsFlag, flags.FieldsFlag(print.IssueFields, []string{
"index", "kind", "title", "state", "updated", "labels",
}),
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
@ -70,7 +68,7 @@ var CmdMilestoneRemoveIssue = cli.Command{
Flags: flags.AllDefaultFlags, Flags: flags.AllDefaultFlags,
} }
func runMilestoneIssueList(_ stdctx.Context, cmd *cli.Command) error { func runMilestoneIssueList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client() client := ctx.Login.Client()
@ -103,7 +101,7 @@ func runMilestoneIssueList(_ stdctx.Context, cmd *cli.Command) error {
} }
issues, _, err := client.ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{ issues, _, err := client.ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{
ListOptions: flags.GetListOptions(), ListOptions: ctx.GetListOptions(),
Milestones: []string{milestone}, Milestones: []string{milestone},
Type: kind, Type: kind,
State: state, State: state,
@ -112,7 +110,7 @@ func runMilestoneIssueList(_ stdctx.Context, cmd *cli.Command) error {
return err return err
} }
fields, err := msIssuesFieldsFlag.GetValues(cmd) fields, err := flags.GetFields(cmd, print.IssueFields)
if err != nil { if err != nil {
return err return err
} }
@ -120,7 +118,7 @@ func runMilestoneIssueList(_ stdctx.Context, cmd *cli.Command) error {
return nil return nil
} }
func runMilestoneIssueAdd(_ stdctx.Context, cmd *cli.Command) error { func runMilestoneIssueAdd(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client() client := ctx.Login.Client()
@ -147,7 +145,7 @@ func runMilestoneIssueAdd(_ stdctx.Context, cmd *cli.Command) error {
return err return err
} }
func runMilestoneIssueRemove(_ stdctx.Context, cmd *cli.Command) error { func runMilestoneIssueRemove(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client() client := ctx.Login.Client()

View File

@ -1,33 +1,26 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 milestones package milestones
import ( import (
stdctx "context"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var fieldsFlag = flags.FieldsFlag(print.MilestoneFields, []string{
"title", "items", "duedate",
})
// CmdMilestonesList represents a sub command of milestones to list milestones // CmdMilestonesList represents a sub command of milestones to list milestones
var CmdMilestonesList = cli.Command{ var CmdMilestonesList = cli.Command{
Name: "list", Name: "list",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Usage: "List milestones of the repository", Usage: "List milestones of the repository",
Description: `List milestones of the repository`, Description: `List milestones of the repository`,
ArgsUsage: " ", // command does not accept arguments
Action: RunMilestonesList, Action: RunMilestonesList,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
fieldsFlag,
&cli.StringFlag{ &cli.StringFlag{
Name: "state", Name: "state",
Usage: "Filter by milestone state (all|open|closed)", Usage: "Filter by milestone state (all|open|closed)",
@ -39,29 +32,21 @@ var CmdMilestonesList = cli.Command{
} }
// RunMilestonesList list milestones // RunMilestonesList list milestones
func RunMilestonesList(_ stdctx.Context, cmd *cli.Command) error { func RunMilestonesList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
fields, err := fieldsFlag.GetValues(cmd)
if err != nil {
return err
}
state := gitea.StateOpen state := gitea.StateOpen
switch ctx.String("state") { switch ctx.String("state") {
case "all": case "all":
state = gitea.StateAll state = gitea.StateAll
if !cmd.IsSet("fields") { // add to default fields
fields = append(fields, "state")
}
case "closed": case "closed":
state = gitea.StateClosed state = gitea.StateClosed
} }
client := ctx.Login.Client() client := ctx.Login.Client()
milestones, _, err := client.ListRepoMilestones(ctx.Owner, ctx.Repo, gitea.ListMilestoneOption{ milestones, _, err := client.ListRepoMilestones(ctx.Owner, ctx.Repo, gitea.ListMilestoneOption{
ListOptions: flags.GetListOptions(), ListOptions: ctx.GetListOptions(),
State: state, State: state,
}) })
@ -69,6 +54,6 @@ func RunMilestonesList(_ stdctx.Context, cmd *cli.Command) error {
return err return err
} }
print.MilestonesList(milestones, ctx.Output, fields) print.MilestonesList(milestones, ctx.Output, state)
return nil return nil
} }

View File

@ -1,61 +1,43 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 milestones package milestones
import ( import (
stdctx "context"
"fmt"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdMilestonesReopen represents a sub command of milestones to open an milestone // CmdMilestonesReopen represents a sub command of milestones to open an milestone
var CmdMilestonesReopen = cli.Command{ var CmdMilestonesReopen = cli.Command{
Name: "reopen", Name: "reopen",
Aliases: []string{"open"}, Aliases: []string{"open"},
Usage: "Change state of one or more milestones to 'open'", Usage: "Change state of an milestone to 'open'",
Description: `Change state of one or more milestones to 'open'`, Description: `Change state of an milestone to 'open'`,
ArgsUsage: "<milestone name> [<milestone name> ...]", ArgsUsage: "<milestone name>",
Action: func(ctx stdctx.Context, cmd *cli.Command) error { Action: func(ctx *cli.Context) error {
return editMilestoneStatus(ctx, cmd, false) return editMilestoneStatus(ctx, false)
}, },
Flags: flags.AllDefaultFlags, Flags: flags.AllDefaultFlags,
} }
func editMilestoneStatus(_ stdctx.Context, cmd *cli.Command, close bool) error { func editMilestoneStatus(cmd *cli.Context, close bool) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() == 0 { client := ctx.Login.Client()
return fmt.Errorf(ctx.Command.ArgsUsage)
}
state := gitea.StateOpen state := gitea.StateOpen
if close { if close {
state = gitea.StateClosed state = gitea.StateClosed
} }
_, _, err := client.EditMilestoneByName(ctx.Owner, ctx.Repo, ctx.Args().First(), gitea.EditMilestoneOption{
client := ctx.Login.Client()
for _, ms := range ctx.Args().Slice() {
opts := gitea.EditMilestoneOption{
State: &state, State: &state,
Title: ms, Title: ctx.Args().First(),
} })
milestone, _, err := client.EditMilestoneByName(ctx.Owner, ctx.Repo, ms, opts)
if err != nil {
return err 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,12 +1,16 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 package cmd
import ( import (
"code.gitea.io/tea/cmd/notifications" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
) )
// CmdNotifications is the main command to operate with notifications // CmdNotifications is the main command to operate with notifications
@ -15,14 +19,65 @@ var CmdNotifications = cli.Command{
Aliases: []string{"notification", "n"}, Aliases: []string{"notification", "n"},
Category: catHelpers, Category: catHelpers,
Usage: "Show notifications", Usage: "Show notifications",
Description: "Show notifications, by default based on the current repo if available", Description: "Show notifications, by default based of the current repo and unread one",
Action: notifications.RunNotificationsList, Action: runNotifications,
Commands: []*cli.Command{ Flags: append([]cli.Flag{
&notifications.CmdNotificationsList, &cli.BoolFlag{
&notifications.CmdNotificationsMarkRead, Name: "all",
&notifications.CmdNotificationsMarkUnread, Aliases: []string{"a"},
&notifications.CmdNotificationsMarkPinned, Usage: "show all notifications of related gitea instance",
&notifications.CmdNotificationsUnpin,
}, },
Flags: notifications.CmdNotificationsList.Flags, &cli.BoolFlag{
Name: "read",
Aliases: []string{"rd"},
Usage: "show read notifications instead unread",
},
&cli.BoolFlag{
Name: "pinned",
Aliases: []string{"pd"},
Usage: "show pinned notifications instead unread",
},
&flags.PaginationPageFlag,
&flags.PaginationLimitFlag,
}, flags.AllDefaultFlags...),
}
func runNotifications(cmd *cli.Context) error {
var news []*gitea.NotificationThread
var err error
ctx := context.InitCommand(cmd)
client := ctx.Login.Client()
listOpts := ctx.GetListOptions()
if listOpts.Page == 0 {
listOpts.Page = 1
}
var status []gitea.NotifyStatus
if ctx.Bool("read") {
status = []gitea.NotifyStatus{gitea.NotifyStatusRead}
}
if ctx.Bool("pinned") {
status = append(status, gitea.NotifyStatusPinned)
}
if ctx.Bool("all") {
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
ListOptions: listOpts,
Status: status,
})
} else {
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
news, _, err = client.ListRepoNotifications(ctx.Owner, ctx.Repo, gitea.ListNotificationOptions{
ListOptions: listOpts,
Status: status,
})
}
if err != nil {
return err
}
print.NotificationsList(news, ctx.Output, ctx.Bool("all"))
return nil
} }

View File

@ -1,107 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package notifications
import (
stdctx "context"
"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/v3"
)
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 stdctx.Context, cmd *cli.Command) error {
var states []gitea.NotifyStatus
statesStr, err := flags.NotificationStateFlag.GetValues(cmd)
if err != nil {
return err
}
for _, s := range statesStr {
states = append(states, gitea.NotifyStatus(s))
}
var types []gitea.NotifySubjectType
typesStr, err := notifyTypeFlag.GetValues(cmd)
if err != nil {
return err
}
for _, t := range typesStr {
types = append(types, gitea.NotifySubjectType(t))
}
return listNotifications(ctx, cmd, states, types)
}
// listNotifications will get the notifications based on status and subject type
func listNotifications(_ stdctx.Context, cmd *cli.Command, 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 := flags.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,139 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package notifications
import (
stdctx "context"
"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/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) error {
ctx := context.InitCommand(cmd)
filter, err := flags.NotificationStateFlag.GetValues(cmd)
if err != nil {
return err
}
if !ctx.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusUnread)}
}
return markNotificationAs(ctx, 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(_ stdctx.Context, cmd *cli.Command) error {
ctx := context.InitCommand(cmd)
filter, err := flags.NotificationStateFlag.GetValues(cmd)
if err != nil {
return err
}
if !ctx.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusRead)}
}
return markNotificationAs(ctx, 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(_ stdctx.Context, cmd *cli.Command) error {
ctx := context.InitCommand(cmd)
filter, err := flags.NotificationStateFlag.GetValues(cmd)
if err != nil {
return err
}
if !ctx.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusUnread)}
}
return markNotificationAs(ctx, 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(_ stdctx.Context, cmd *cli.Command) error {
ctx := context.InitCommand(cmd)
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(ctx, 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,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 package cmd
import ( import (
stdctx "context"
"path" "path"
"strings" "strings"
@ -13,7 +13,7 @@ import (
local_git "code.gitea.io/tea/modules/git" local_git "code.gitea.io/tea/modules/git"
"github.com/skratchdot/open-golang/open" "github.com/skratchdot/open-golang/open"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdOpen represents a sub command of issues to open issue on the web browser // CmdOpen represents a sub command of issues to open issue on the web browser
@ -27,7 +27,7 @@ var CmdOpen = cli.Command{
Flags: append([]cli.Flag{}, flags.LoginRepoFlags...), Flags: append([]cli.Flag{}, flags.LoginRepoFlags...),
} }
func runOpen(_ stdctx.Context, cmd *cli.Command) error { func runOpen(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
@ -74,5 +74,6 @@ func runOpen(_ stdctx.Context, cmd *cli.Command) error {
suffix = number suffix = number
} }
return open.Run(path.Join(ctx.GetRemoteRepoHTMLURL(), suffix)) u := path.Join(ctx.Login.URL, ctx.Owner, ctx.Repo, suffix)
return open.Run(u)
} }

View File

@ -1,16 +1,15 @@
// Copyright 2019 The Gitea Authors. All rights reserved. // 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 package cmd
import ( import (
stdctx "context" "fmt"
"code.gitea.io/tea/cmd/organizations" "code.gitea.io/tea/cmd/organizations"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdOrgs represents handle organization // CmdOrgs represents handle organization
@ -22,28 +21,19 @@ var CmdOrgs = cli.Command{
Description: "Show organization details", Description: "Show organization details",
ArgsUsage: "[<organization>]", ArgsUsage: "[<organization>]",
Action: runOrganizations, Action: runOrganizations,
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&organizations.CmdOrganizationList, &organizations.CmdOrganizationList,
&organizations.CmdOrganizationCreate,
&organizations.CmdOrganizationDelete, &organizations.CmdOrganizationDelete,
}, },
Flags: organizations.CmdOrganizationList.Flags,
} }
func runOrganizations(ctx stdctx.Context, cmd *cli.Command) error { func runOrganizations(ctx *cli.Context) error {
teaCtx := context.InitCommand(cmd) if ctx.Args().Len() == 1 {
if teaCtx.Args().Len() == 1 { return runOrganizationDetail(ctx.Args().First())
return runOrganizationDetail(teaCtx)
} }
return organizations.RunOrganizationList(ctx, cmd) return organizations.RunOrganizationList(ctx)
} }
func runOrganizationDetail(ctx *context.TeaContext) error { func runOrganizationDetail(path string) error {
org, _, err := ctx.Login.Client().GetOrg(ctx.Args().First()) return fmt.Errorf("Not yet implemented")
if err != nil {
return err
}
print.OrganizationDetails(org)
return nil
} }

View File

@ -1,90 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organizations
import (
"fmt"
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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,15 +1,14 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 organizations package organizations
import ( import (
stdctx "context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdOrganizationDelete represents a sub command of organizations to delete a given user organization // CmdOrganizationDelete represents a sub command of organizations to delete a given user organization
@ -20,14 +19,10 @@ var CmdOrganizationDelete = cli.Command{
Description: "Delete users organizations", Description: "Delete users organizations",
ArgsUsage: "<organization name>", ArgsUsage: "<organization name>",
Action: RunOrganizationDelete, Action: RunOrganizationDelete,
Flags: []cli.Flag{
&flags.LoginFlag,
&flags.RemoteFlag,
},
} }
// RunOrganizationDelete delete user organization // RunOrganizationDelete delete user organization
func RunOrganizationDelete(_ stdctx.Context, cmd *cli.Command) error { func RunOrganizationDelete(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
client := ctx.Login.Client() client := ctx.Login.Client()

View File

@ -1,16 +1,16 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 organizations package organizations
import ( import (
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
) )
// CmdOrganizationList represents a sub command of organizations to list users organizations // CmdOrganizationList represents a sub command of organizations to list users organizations
@ -19,7 +19,6 @@ var CmdOrganizationList = cli.Command{
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Usage: "List Organizations", Usage: "List Organizations",
Description: "List users organizations", Description: "List users organizations",
ArgsUsage: " ", // command does not accept arguments
Action: RunOrganizationList, Action: RunOrganizationList,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&flags.PaginationPageFlag, &flags.PaginationPageFlag,
@ -28,12 +27,12 @@ var CmdOrganizationList = cli.Command{
} }
// RunOrganizationList list user organizations // RunOrganizationList list user organizations
func RunOrganizationList(_ stdctx.Context, cmd *cli.Command) error { func RunOrganizationList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
client := ctx.Login.Client() client := ctx.Login.Client()
userOrganizations, _, err := client.ListUserOrgs(ctx.Login.User, gitea.ListOrgsOptions{ userOrganizations, _, err := client.ListUserOrgs(ctx.Login.User, gitea.ListOrgsOptions{
ListOptions: flags.GetListOptions(), ListOptions: ctx.GetListOptions(),
}) })
if err != nil { if err != nil {
return err return err

View File

@ -1,10 +1,10 @@
// Copyright 2018 The Gitea Authors. All rights reserved. // 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 package cmd
import ( import (
stdctx "context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/pulls" "code.gitea.io/tea/cmd/pulls"
@ -15,7 +15,7 @@ import (
"code.gitea.io/tea/modules/workaround" "code.gitea.io/tea/modules/workaround"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdPulls is the main command to operate on PRs // CmdPulls is the main command to operate on PRs
@ -30,10 +30,10 @@ var CmdPulls = cli.Command{
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "comments", Name: "comments",
Usage: "Whether to display comments (will prompt if not provided & run interactively)", Usage: "Wether to display comments (will prompt if not provided & run interactively)",
}, },
}, pulls.CmdPullsList.Flags...), }, pulls.CmdPullsList.Flags...),
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&pulls.CmdPullsList, &pulls.CmdPullsList,
&pulls.CmdPullsCheckout, &pulls.CmdPullsCheckout,
&pulls.CmdPullsClean, &pulls.CmdPullsClean,
@ -43,18 +43,17 @@ var CmdPulls = cli.Command{
&pulls.CmdPullsReview, &pulls.CmdPullsReview,
&pulls.CmdPullsApprove, &pulls.CmdPullsApprove,
&pulls.CmdPullsReject, &pulls.CmdPullsReject,
&pulls.CmdPullsMerge,
}, },
} }
func runPulls(ctx stdctx.Context, cmd *cli.Command) error { func runPulls(ctx *cli.Context) error {
if cmd.Args().Len() == 1 { if ctx.Args().Len() == 1 {
return runPullDetail(ctx, cmd, cmd.Args().First()) return runPullDetail(ctx, ctx.Args().First())
} }
return pulls.RunPullsList(ctx, cmd) return pulls.RunPullsList(ctx)
} }
func runPullDetail(_ stdctx.Context, cmd *cli.Command, index string) error { func runPullDetail(cmd *cli.Context, index string) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
idx, err := utils.ArgToIndex(index) idx, err := utils.ArgToIndex(index)
@ -71,9 +70,7 @@ func runPullDetail(_ stdctx.Context, cmd *cli.Command, index string) error {
return err return err
} }
reviews, _, err := client.ListPullReviews(ctx.Owner, ctx.Repo, idx, gitea.ListPullReviewsOptions{ reviews, _, err := client.ListPullReviews(ctx.Owner, ctx.Repo, idx, gitea.ListPullReviewsOptions{})
ListOptions: gitea.ListOptions{Page: -1},
})
if err != nil { if err != nil {
fmt.Printf("error while loading reviews: %v\n", err) fmt.Printf("error while loading reviews: %v\n", err)
} }

View File

@ -1,5 +1,6 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 pulls package pulls
@ -7,14 +8,13 @@ import (
"fmt" "fmt"
"strings" "strings"
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v3"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
) )
// CmdPullsApprove approves a PR // CmdPullsApprove approves a PR
@ -24,7 +24,7 @@ var CmdPullsApprove = cli.Command{
Usage: "Approve a pull request", Usage: "Approve a pull request",
Description: "Approve a pull request", Description: "Approve a pull request",
ArgsUsage: "<pull index> [<comment>]", ArgsUsage: "<pull index> [<comment>]",
Action: func(_ stdctx.Context, cmd *cli.Command) error { Action: func(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})

View File

@ -1,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 pulls package pulls
import ( import (
stdctx "context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
@ -13,7 +13,7 @@ import (
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdPullsCheckout is a command to locally checkout the given PR // CmdPullsCheckout is a command to locally checkout the given PR
@ -33,12 +33,9 @@ var CmdPullsCheckout = cli.Command{
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
func runPullsCheckout(_ stdctx.Context, cmd *cli.Command) error { func runPullsCheckout(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{ ctx.Ensure(context.CtxRequirement{LocalRepo: true})
LocalRepo: true,
RemoteRepo: true,
})
if ctx.Args().Len() != 1 { if ctx.Args().Len() != 1 {
return fmt.Errorf("Must specify a PR index") return fmt.Errorf("Must specify a PR index")
} }
@ -47,8 +44,5 @@ func runPullsCheckout(_ stdctx.Context, cmd *cli.Command) error {
return err return err
} }
if err := task.PullCheckout(ctx.Login, ctx.Owner, ctx.Repo, ctx.Bool("branch"), idx, interact.PromptPassword); err != nil && !interact.IsQuitting(err) { return task.PullCheckout(ctx.Login, ctx.Owner, ctx.Repo, ctx.Bool("branch"), idx, interact.PromptPassword)
return err
}
return nil
} }

View File

@ -1,19 +1,19 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 pulls package pulls
import ( import (
"fmt" "fmt"
stdctx "context"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v3"
"github.com/urfave/cli/v2"
) )
// CmdPullsClean removes the remote and local feature branches, if a PR is merged. // CmdPullsClean removes the remote and local feature branches, if a PR is merged.
@ -31,7 +31,7 @@ var CmdPullsClean = cli.Command{
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
func runPullsClean(_ stdctx.Context, cmd *cli.Command) error { func runPullsClean(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{LocalRepo: true}) ctx.Ensure(context.CtxRequirement{LocalRepo: true})
if ctx.Args().Len() != 1 { if ctx.Args().Len() != 1 {
@ -43,8 +43,5 @@ func runPullsClean(_ stdctx.Context, cmd *cli.Command) error {
return err return err
} }
if err := task.PullClean(ctx.Login, ctx.Owner, ctx.Repo, idx, ctx.Bool("ignore-sha"), interact.PromptPassword); err != nil && !interact.IsQuitting(err) { return task.PullClean(ctx.Login, ctx.Owner, ctx.Repo, idx, ctx.Bool("ignore-sha"), interact.PromptPassword)
return err
}
return nil
} }

View File

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

View File

@ -1,17 +1,16 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 pulls package pulls
import ( import (
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/task"
"github.com/urfave/cli/v3"
"github.com/urfave/cli/v2"
) )
// CmdPullsCreate creates a pull request // CmdPullsCreate creates a pull request
@ -19,54 +18,42 @@ var CmdPullsCreate = cli.Command{
Name: "create", Name: "create",
Aliases: []string{"c"}, Aliases: []string{"c"},
Usage: "Create a pull-request", Usage: "Create a pull-request",
Description: "Create a pull-request in the current repo", Description: "Create a pull-request",
Action: runPullsCreate, Action: runPullsCreate,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "head", Name: "head",
Usage: "Branch name of the PR source (default is current one). To specify a different head repo, use <user>:<branch>", Usage: "Set head branch (default is current one)",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "base", Name: "base",
Aliases: []string{"b"}, Aliases: []string{"b"},
Usage: "Branch name of the PR target (default is repos default branch)", Usage: "Set base branch (default is default branch)",
}, },
&cli.BoolFlag{ }, flags.IssuePREditFlags...),
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(_ stdctx.Context, cmd *cli.Command) error { func runPullsCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{LocalRepo: true})
// no args -> interactive mode // no args -> interactive mode
if ctx.NumFlags() == 0 { if ctx.NumFlags() == 0 {
if err := interact.CreatePull(ctx); err != nil && !interact.IsQuitting(err) { return interact.CreatePull(ctx.Login, ctx.Owner, ctx.Repo)
return err
}
return nil
} }
// else use args to create PR // else use args to create PR
opts, err := flags.GetIssuePRCreateFlags(ctx) opts, err := flags.GetIssuePREditFlags(ctx)
if err != nil { if err != nil {
return err return err
} }
var allowMaintainerEdits *bool
if ctx.IsSet("allow-maintainer-edits") {
allowMaintainerEdits = gitea.OptionalBool(ctx.Bool("allow-maintainer-edits"))
}
return task.CreatePull( return task.CreatePull(
ctx, ctx.Login,
ctx.Owner,
ctx.Repo,
ctx.String("base"), ctx.String("base"),
ctx.String("head"), ctx.String("head"),
allowMaintainerEdits,
opts, opts,
) )
} }

View File

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

View File

@ -1,21 +1,17 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 pulls package pulls
import ( import (
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"github.com/urfave/cli/v3"
)
var pullFieldsFlag = flags.FieldsFlag(print.PullFields, []string{ "code.gitea.io/sdk/gitea"
"index", "title", "state", "author", "milestone", "updated", "labels", "github.com/urfave/cli/v2"
}) )
// CmdPullsList represents a sub command of issues to list pulls // CmdPullsList represents a sub command of issues to list pulls
var CmdPullsList = cli.Command{ var CmdPullsList = cli.Command{
@ -23,13 +19,12 @@ var CmdPullsList = cli.Command{
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Usage: "List pull requests of the repository", Usage: "List pull requests of the repository",
Description: `List pull requests of the repository`, Description: `List pull requests of the repository`,
ArgsUsage: " ", // command does not accept arguments
Action: RunPullsList, Action: RunPullsList,
Flags: append([]cli.Flag{pullFieldsFlag}, flags.PRListingFlags...), Flags: flags.IssuePRFlags,
} }
// RunPullsList return list of pulls // RunPullsList return list of pulls
func RunPullsList(_ stdctx.Context, cmd *cli.Command) error { func RunPullsList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
@ -51,11 +46,6 @@ func RunPullsList(_ stdctx.Context, cmd *cli.Command) error {
return err return err
} }
fields, err := pullFieldsFlag.GetValues(cmd) print.PullsList(prs, ctx.Output)
if err != nil {
return err
}
print.PullsList(prs, ctx.Output, fields)
return nil return nil
} }

View File

@ -1,66 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pulls
import (
stdctx "context"
"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/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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
if err := interact.MergePull(ctx); err != nil && !interact.IsQuitting(err) {
return err
}
return nil
}
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,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 pulls package pulls
import ( import (
stdctx "context"
"fmt" "fmt"
"strings" "strings"
@ -14,7 +14,7 @@ import (
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdPullsReject requests changes to a PR // CmdPullsReject requests changes to a PR
@ -23,7 +23,7 @@ var CmdPullsReject = cli.Command{
Usage: "Request changes to a pull request", Usage: "Request changes to a pull request",
Description: "Request changes to a pull request", Description: "Request changes to a pull request",
ArgsUsage: "<pull index> <reason>", ArgsUsage: "<pull index> <reason>",
Action: func(_ stdctx.Context, cmd *cli.Command) error { Action: func(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})

View File

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

View File

@ -1,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 pulls package pulls
import ( import (
stdctx "context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
@ -12,7 +12,7 @@ import (
"code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/interact"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdPullsReview starts an interactive review session // CmdPullsReview starts an interactive review session
@ -21,12 +21,12 @@ var CmdPullsReview = cli.Command{
Usage: "Interactively review a pull request", Usage: "Interactively review a pull request",
Description: "Interactively review a pull request", Description: "Interactively review a pull request",
ArgsUsage: "<pull index>", ArgsUsage: "<pull index>",
Action: func(_ stdctx.Context, cmd *cli.Command) error { Action: func(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
if ctx.Args().Len() != 1 { if ctx.Args().Len() != 1 {
return fmt.Errorf("must specify a PR index") return fmt.Errorf("Must specify a PR index")
} }
idx, err := utils.ArgToIndex(ctx.Args().First()) idx, err := utils.ArgToIndex(ctx.Args().First())
@ -34,10 +34,7 @@ var CmdPullsReview = cli.Command{
return err return err
} }
if err := interact.ReviewPull(ctx, idx); err != nil && !interact.IsQuitting(err) { return interact.ReviewPull(ctx, idx)
return err
}
return nil
}, },
Flags: flags.AllDefaultFlags, Flags: flags.AllDefaultFlags,
} }

View File

@ -1,5 +1,6 @@
// Copyright 2018 The Gitea Authors. All rights reserved. // 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 package cmd
@ -7,7 +8,7 @@ import (
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/cmd/releases" "code.gitea.io/tea/cmd/releases"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdReleases represents to login a gitea server. // CmdReleases represents to login a gitea server.
@ -18,14 +19,12 @@ var CmdReleases = cli.Command{
Category: catEntities, Category: catEntities,
Usage: "Manage releases", Usage: "Manage releases",
Description: "Manage releases", Description: "Manage releases",
ArgsUsage: " ", // command does not accept arguments
Action: releases.RunReleasesList, Action: releases.RunReleasesList,
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&releases.CmdReleaseList, &releases.CmdReleaseList,
&releases.CmdReleaseCreate, &releases.CmdReleaseCreate,
&releases.CmdReleaseDelete, &releases.CmdReleaseDelete,
&releases.CmdReleaseEdit, &releases.CmdReleaseEdit,
&CmdReleaseAttachments,
}, },
Flags: flags.AllDefaultFlags, Flags: flags.AllDefaultFlags,
} }

View File

@ -1,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 releases package releases
import ( import (
stdctx "context"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
@ -14,7 +14,7 @@ import (
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdReleaseCreate represents a sub command of Release to create release // CmdReleaseCreate represents a sub command of Release to create release
@ -22,17 +22,16 @@ var CmdReleaseCreate = cli.Command{
Name: "create", Name: "create",
Aliases: []string{"c"}, Aliases: []string{"c"},
Usage: "Create a release", Usage: "Create a release",
Description: `Create a release for a new or existing git tag`, Description: `Create a release`,
ArgsUsage: "[<tag>]",
Action: runReleaseCreate, Action: runReleaseCreate,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "tag", Name: "tag",
Usage: "Tag name. If the tag does not exist yet, it will be created by Gitea", Usage: "Tag name",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "target", Name: "target",
Usage: "Target branch name or commit hash. Defaults to the default branch of the repo", Usage: "Target refs, branch name or commit id",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "title", Name: "title",
@ -44,11 +43,6 @@ var CmdReleaseCreate = cli.Command{
Aliases: []string{"n"}, Aliases: []string{"n"},
Usage: "Release notes", Usage: "Release notes",
}, },
&cli.StringFlag{
Name: "note-file",
Aliases: []string{"f"},
Usage: "Release notes file name. If set, --note is ignored.",
},
&cli.BoolFlag{ &cli.BoolFlag{
Name: "draft", Name: "draft",
Aliases: []string{"d"}, Aliases: []string{"d"},
@ -62,41 +56,24 @@ var CmdReleaseCreate = cli.Command{
&cli.StringSliceFlag{ &cli.StringSliceFlag{
Name: "asset", Name: "asset",
Aliases: []string{"a"}, Aliases: []string{"a"},
Usage: "Path to file attachment. Can be specified multiple times", Usage: "List of files to attach",
}, },
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
func runReleaseCreate(_ stdctx.Context, cmd *cli.Command) error { func runReleaseCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
tag := ctx.String("tag")
if cmd.Args().Present() {
if len(tag) != 0 {
return fmt.Errorf("ambiguous arguments: provide tagname via --tag or argument, but not both")
}
tag = cmd.Args().First()
}
notestring := ctx.String("note")
notefile := ctx.String("note-file")
if notefile != "" {
notebytes, err := os.ReadFile(notefile)
if err != nil {
return fmt.Errorf("unable to read the note file")
}
notestring = string(notebytes)
}
release, resp, err := ctx.Login.Client().CreateRelease(ctx.Owner, ctx.Repo, gitea.CreateReleaseOption{ release, resp, err := ctx.Login.Client().CreateRelease(ctx.Owner, ctx.Repo, gitea.CreateReleaseOption{
TagName: tag, TagName: ctx.String("tag"),
Target: ctx.String("target"), Target: ctx.String("target"),
Title: ctx.String("title"), Title: ctx.String("title"),
Note: notestring, Note: ctx.String("note"),
IsDraft: ctx.Bool("draft"), IsDraft: ctx.Bool("draft"),
IsPrerelease: ctx.Bool("prerelease"), IsPrerelease: ctx.Bool("prerelease"),
}) })
if err != nil { if err != nil {
if resp != nil && resp.StatusCode == http.StatusConflict { if resp != nil && resp.StatusCode == http.StatusConflict {
return fmt.Errorf("There already is a release for this tag") return fmt.Errorf("There already is a release for this tag")

View File

@ -1,25 +1,25 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 releases package releases
import ( import (
stdctx "context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdReleaseDelete represents a sub command of Release to delete a release // CmdReleaseDelete represents a sub command of Release to delete a release
var CmdReleaseDelete = cli.Command{ var CmdReleaseDelete = cli.Command{
Name: "delete", Name: "delete",
Aliases: []string{"rm"}, Aliases: []string{"rm"},
Usage: "Delete one or more releases", Usage: "Delete a release",
Description: `Delete one or more releases`, Description: `Delete a release`,
ArgsUsage: "<release tag> [<release tag>...]", ArgsUsage: "<release tag>",
Action: runReleaseDelete, Action: runReleaseDelete,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
@ -34,13 +34,14 @@ var CmdReleaseDelete = cli.Command{
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
func runReleaseDelete(_ stdctx.Context, cmd *cli.Command) error { func runReleaseDelete(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client() client := ctx.Login.Client()
if !ctx.Args().Present() { tag := ctx.Args().First()
fmt.Println("Release tag needed to edit") if len(tag) == 0 {
fmt.Println("Release tag needed to delete")
return nil return nil
} }
@ -49,11 +50,14 @@ func runReleaseDelete(_ stdctx.Context, cmd *cli.Command) error {
return nil return nil
} }
for _, tag := range ctx.Args().Slice() {
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client) release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
if err != nil { if err != nil {
return err return err
} }
if release == nil {
return nil
}
_, err = client.DeleteRelease(ctx.Owner, ctx.Repo, release.ID) _, err = client.DeleteRelease(ctx.Owner, ctx.Repo, release.ID)
if err != nil { if err != nil {
return err return err
@ -63,7 +67,6 @@ func runReleaseDelete(_ stdctx.Context, cmd *cli.Command) error {
_, err = client.DeleteTag(ctx.Owner, ctx.Repo, tag) _, err = client.DeleteTag(ctx.Owner, ctx.Repo, tag)
return err return err
} }
}
return nil return nil
} }

View File

@ -1,5 +1,6 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 releases package releases
@ -7,21 +8,20 @@ import (
"fmt" "fmt"
"strings" "strings"
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"github.com/urfave/cli/v3"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
) )
// CmdReleaseEdit represents a sub command of Release to edit releases // CmdReleaseEdit represents a sub command of Release to edit releases
var CmdReleaseEdit = cli.Command{ var CmdReleaseEdit = cli.Command{
Name: "edit", Name: "edit",
Aliases: []string{"e"}, Aliases: []string{"e"},
Usage: "Edit one or more releases", Usage: "Edit a release",
Description: `Edit one or more releases`, Description: `Edit a release`,
ArgsUsage: "<release tag> [<release tag>...]", ArgsUsage: "<release tag>",
Action: runReleaseEdit, Action: runReleaseEdit,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
@ -57,11 +57,25 @@ var CmdReleaseEdit = cli.Command{
}, flags.AllDefaultFlags...), }, flags.AllDefaultFlags...),
} }
func runReleaseEdit(_ stdctx.Context, cmd *cli.Command) error { func runReleaseEdit(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
client := ctx.Login.Client() client := ctx.Login.Client()
tag := ctx.Args().First()
if len(tag) == 0 {
fmt.Println("Release tag needed to edit")
return nil
}
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
if err != nil {
return err
}
if release == nil {
return nil
}
var isDraft, isPre *bool var isDraft, isPre *bool
if ctx.IsSet("draft") { if ctx.IsSet("draft") {
isDraft = gitea.OptionalBool(strings.ToLower(ctx.String("draft"))[:1] == "t") isDraft = gitea.OptionalBool(strings.ToLower(ctx.String("draft"))[:1] == "t")
@ -70,17 +84,6 @@ func runReleaseEdit(_ stdctx.Context, cmd *cli.Command) error {
isPre = gitea.OptionalBool(strings.ToLower(ctx.String("prerelease"))[:1] == "t") 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{ _, _, err = client.EditRelease(ctx.Owner, ctx.Repo, release.ID, gitea.EditReleaseOption{
TagName: ctx.String("tag"), TagName: ctx.String("tag"),
Target: ctx.String("target"), Target: ctx.String("target"),
@ -89,9 +92,5 @@ func runReleaseEdit(_ stdctx.Context, cmd *cli.Command) error {
IsDraft: isDraft, IsDraft: isDraft,
IsPrerelease: isPre, IsPrerelease: isPre,
}) })
if err != nil {
return err return err
} }
}
return nil
}

View File

@ -1,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 releases package releases
import ( import (
stdctx "context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
@ -12,7 +12,7 @@ import (
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdReleaseList represents a sub command of Release to list releases // CmdReleaseList represents a sub command of Release to list releases
@ -21,7 +21,6 @@ var CmdReleaseList = cli.Command{
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Usage: "List Releases", Usage: "List Releases",
Description: "List Releases", Description: "List Releases",
ArgsUsage: " ", // command does not accept arguments
Action: RunReleasesList, Action: RunReleasesList,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&flags.PaginationPageFlag, &flags.PaginationPageFlag,
@ -30,12 +29,12 @@ var CmdReleaseList = cli.Command{
} }
// RunReleasesList list releases // RunReleasesList list releases
func RunReleasesList(_ stdctx.Context, cmd *cli.Command) error { func RunReleasesList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
releases, _, err := ctx.Login.Client().ListReleases(ctx.Owner, ctx.Repo, gitea.ListReleasesOptions{ releases, _, err := ctx.Login.Client().ListReleases(ctx.Owner, ctx.Repo, gitea.ListReleasesOptions{
ListOptions: flags.GetListOptions(), ListOptions: ctx.GetListOptions(),
}) })
if err != nil { if err != nil {
return err return err
@ -46,19 +45,19 @@ func RunReleasesList(_ stdctx.Context, cmd *cli.Command) error {
} }
func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) { func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) {
rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{ rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{})
ListOptions: gitea.ListOptions{Page: -1},
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(rl) == 0 { if len(rl) == 0 {
return nil, fmt.Errorf("Repo does not have any release") fmt.Println("Repo does not have any release")
return nil, nil
} }
for _, r := range rl { for _, r := range rl {
if r.TagName == tag { if r.TagName == tag {
return r, nil return r, nil
} }
} }
return nil, fmt.Errorf("Release tag does not exist") fmt.Println("Release tag does not exist")
return nil, nil
} }

View File

@ -1,18 +1,17 @@
// Copyright 2019 The Gitea Authors. All rights reserved. // 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 package cmd
import ( import (
stdctx "context"
"code.gitea.io/tea/cmd/repos" "code.gitea.io/tea/cmd/repos"
"code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils" "code.gitea.io/tea/modules/utils"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdRepos represents to login a gitea server. // CmdRepos represents to login a gitea server.
@ -24,26 +23,22 @@ var CmdRepos = cli.Command{
Description: "Show repository details", Description: "Show repository details",
ArgsUsage: "[<repo owner>/<repo name>]", ArgsUsage: "[<repo owner>/<repo name>]",
Action: runRepos, Action: runRepos,
Commands: []*cli.Command{ Subcommands: []*cli.Command{
&repos.CmdReposList, &repos.CmdReposList,
&repos.CmdReposSearch, &repos.CmdReposSearch,
&repos.CmdRepoCreate, &repos.CmdRepoCreate,
&repos.CmdRepoCreateFromTemplate,
&repos.CmdRepoFork,
&repos.CmdRepoMigrate,
&repos.CmdRepoRm,
}, },
Flags: repos.CmdReposListFlags, Flags: repos.CmdReposListFlags,
} }
func runRepos(ctx stdctx.Context, cmd *cli.Command) error { func runRepos(ctx *cli.Context) error {
if cmd.Args().Len() == 1 { if ctx.Args().Len() == 1 {
return runRepoDetail(ctx, cmd, cmd.Args().First()) return runRepoDetail(ctx, ctx.Args().First())
} }
return repos.RunReposList(ctx, cmd) return repos.RunReposList(ctx)
} }
func runRepoDetail(_ stdctx.Context, cmd *cli.Command, path string) error { func runRepoDetail(cmd *cli.Context, path string) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
client := ctx.Login.Client() client := ctx.Login.Client()
repoOwner, repoName := utils.GetOwnerAndRepo(path, ctx.Owner) repoOwner, repoName := utils.GetOwnerAndRepo(path, ctx.Owner)

View File

@ -1,10 +1,10 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 repos package repos
import ( import (
stdctx "context"
"fmt" "fmt"
"code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/flags"
@ -12,7 +12,7 @@ import (
"code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/print"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdRepoCreate represents a sub command of repos to create one // CmdRepoCreate represents a sub command of repos to create one
@ -21,7 +21,6 @@ var CmdRepoCreate = cli.Command{
Aliases: []string{"c"}, Aliases: []string{"c"},
Usage: "Create a repository", Usage: "Create a repository",
Description: "Create a repository", Description: "Create a repository",
ArgsUsage: " ", // command does not accept arguments
Action: runRepoCreate, Action: runRepoCreate,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
@ -80,50 +79,16 @@ var CmdRepoCreate = cli.Command{
Required: false, Required: false,
Usage: "use custom default branch (need --init)", Usage: "use custom default branch (need --init)",
}, },
&cli.BoolFlag{
Name: "template",
Usage: "make repo a template repo",
},
&cli.StringFlag{
Name: "trustmodel",
Usage: "select trust model (committer,collaborator,collaborator+committer)",
},
&cli.StringFlag{
Name: "object-format",
Required: false,
Usage: "select git object format (sha1,sha256)",
Validator: func(v string) error {
if v != "sha1" && v != "sha256" {
return fmt.Errorf("invalid object format '%s', must be either 'sha1' or 'sha256'", v)
}
return nil
},
},
}, flags.LoginOutputFlags...), }, flags.LoginOutputFlags...),
} }
func runRepoCreate(_ stdctx.Context, cmd *cli.Command) error { func runRepoCreate(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
client := ctx.Login.Client() client := ctx.Login.Client()
var ( var (
repo *gitea.Repository repo *gitea.Repository
err error err error
trustmodel gitea.TrustModel
) )
if ctx.IsSet("trustmodel") {
switch ctx.String("trustmodel") {
case "committer":
trustmodel = gitea.TrustModelCommitter
case "collaborator":
trustmodel = gitea.TrustModelCollaborator
case "collaborator+committer":
trustmodel = gitea.TrustModelCollaboratorCommitter
default:
return fmt.Errorf("unknown trustmodel type '%s'", ctx.String("trustmodel"))
}
}
opts := gitea.CreateRepoOption{ opts := gitea.CreateRepoOption{
Name: ctx.String("name"), Name: ctx.String("name"),
Description: ctx.String("description"), Description: ctx.String("description"),
@ -134,9 +99,6 @@ func runRepoCreate(_ stdctx.Context, cmd *cli.Command) error {
License: ctx.String("license"), License: ctx.String("license"),
Readme: ctx.String("readme"), Readme: ctx.String("readme"),
DefaultBranch: ctx.String("branch"), DefaultBranch: ctx.String("branch"),
Template: ctx.Bool("template"),
TrustModel: trustmodel,
ObjectFormatName: ctx.String("object-format"),
} }
if len(ctx.String("owner")) != 0 { if len(ctx.String("owner")) != 0 {
repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts) repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts)

View File

@ -1,121 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
import (
"fmt"
stdctx "context"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"code.gitea.io/tea/modules/print"
"code.gitea.io/tea/modules/utils"
"github.com/urfave/cli/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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,88 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
import (
"context"
"fmt"
"os"
"testing"
"time"
"code.gitea.io/sdk/gitea"
"code.gitea.io/tea/modules/task"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3"
)
func TestCreateRepoObjectFormat(t *testing.T) {
giteaURL := os.Getenv("GITEA_TEA_TEST_URL")
if giteaURL == "" {
t.Skip("GITEA_TEA_TEST_URL is not set, skipping test")
}
timestamp := time.Now().Unix()
tests := []struct {
name string
args []string
wantOpts gitea.CreateRepoOption
wantErr bool
errContains string
}{
{
name: "create repo with sha1 object format",
args: []string{"--name", fmt.Sprintf("test-sha1-%d", timestamp), "--object-format", "sha1"},
wantOpts: gitea.CreateRepoOption{
Name: fmt.Sprintf("test-sha1-%d", timestamp),
ObjectFormatName: "sha1",
},
wantErr: false,
},
{
name: "create repo with sha256 object format",
args: []string{"--name", fmt.Sprintf("test-sha256-%d", timestamp), "--object-format", "sha256"},
wantOpts: gitea.CreateRepoOption{
Name: fmt.Sprintf("test-sha256-%d", timestamp),
ObjectFormatName: "sha256",
},
wantErr: false,
},
{
name: "create repo with invalid object format",
args: []string{"--name", fmt.Sprintf("test-invalid-%d", timestamp), "--object-format", "invalid"},
wantErr: true,
errContains: "invalid object format",
},
}
giteaUserName := os.Getenv("GITEA_TEA_TEST_USERNAME")
giteaUserPasword := os.Getenv("GITEA_TEA_TEST_PASSWORD")
err := task.CreateLogin("test", "", giteaUserName, giteaUserPasword, "", "", "", giteaURL, "", "", true, false, false, false)
if err != nil && err.Error() != "login name 'test' has already been used" {
t.Fatal(err)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reposCmd := &cli.Command{
Name: "repos",
Commands: []*cli.Command{&CmdRepoCreate},
}
tt.args = append(tt.args, "--login", "test")
args := append([]string{"repos", "create"}, tt.args...)
err := reposCmd.Run(context.Background(), args)
if tt.wantErr {
assert.Error(t, err)
if tt.errContains != "" {
assert.Contains(t, err.Error(), tt.errContains)
}
return
}
assert.NoError(t, err)
})
}
}

View File

@ -1,86 +0,0 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
import (
stdctx "context"
"fmt"
"code.gitea.io/tea/cmd/flags"
"code.gitea.io/tea/modules/context"
"github.com/charmbracelet/huh"
"github.com/urfave/cli/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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
if err := huh.NewInput().
Title(fmt.Sprintf("Confirm the deletion of the repository '%s' by typing its name: ", repoSlug)).
Validate(huh.ValidateNotEmpty()).
Value(&enteredRepoSlug).
Run(); 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,5 +1,6 @@
// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2020 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 repos package repos
@ -7,7 +8,7 @@ import (
"fmt" "fmt"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var typeFilterFlag = cli.StringFlag{ var typeFilterFlag = cli.StringFlag{
@ -17,8 +18,8 @@ var typeFilterFlag = cli.StringFlag{
Usage: "Filter by type: fork, mirror, source", Usage: "Filter by type: fork, mirror, source",
} }
func getTypeFilter(cmd *cli.Command) (filter gitea.RepoType, err error) { func getTypeFilter(ctx *cli.Context) (filter gitea.RepoType, err error) {
t := cmd.String("type") t := ctx.String("type")
filter = gitea.RepoTypeNone filter = gitea.RepoTypeNone
switch t { switch t {
case "": case "":

View File

@ -1,59 +0,0 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repos
import (
stdctx "context"
"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/v3"
)
// 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(_ stdctx.Context, cmd *cli.Command) 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
}

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