mirror of https://gitea.com/gitea/tea.git
Compare commits
328 Commits
Author | SHA1 | Date |
---|---|---|
Matheus Sampaio Queiroga | 2984ad4964 | |
Renovate Bot | a35bf931ae | |
Renovate Bot | edf9f23390 | |
Renovate Bot | 0297738b33 | |
Renovate Bot | 1983c3bbd9 | |
Renovate Bot | d289c290bb | |
harryzcy | 100c5a9eee | |
Renovate Bot | 4ba3139224 | |
Renovate Bot | dd80463cf5 | |
techknowlogick | 93480ac98e | |
techknowlogick | f7e2c6f89b | |
Jake Hillion | 493b29afbf | |
Dan Čermák | 723b0553c5 | |
danebou | 4c0cef090d | |
leonard.vimond | 20479663f0 | |
Renovate Bot | 22370698bf | |
Renovate Bot | 6d5e8d3b58 | |
Renovate Bot | 18abf24dca | |
Renovate Bot | 7cb6a84098 | |
techknowlogick | 7e700e36f8 | |
Renovate Bot | 61bdc14184 | |
Renovate Bot | 16b37cc6da | |
Renovate Bot | b5d7119ab4 | |
Renovate Bot | 610d04fc85 | |
Renovate Bot | 6696e945c6 | |
Renovate Bot | 5158adbcf2 | |
Lunny Xiao | 6e473ec601 | |
Renovate Bot | 115f25e85f | |
Renovate Bot | 656d48ec8d | |
Renovate Bot | 2cb450401c | |
techknowlogick | 5236a89d4c | |
techknowlogick | 48d2997a63 | |
techknowlogick | 75a7d4b7cc | |
techknowlogick | 1e3043ce6f | |
techknowlogick | 218b991069 | |
techknowlogick | 16a3594474 | |
techknowlogick | b1b31da7c8 | |
Lunny Xiao | 36672485d1 | |
Renovate Bot | 4fedaaafe1 | |
Renovate Bot | 6ae3718574 | |
Renovate Bot | c4f6231c49 | |
Renovate Bot | 76f7f901fc | |
Renovate Bot | 005beca233 | |
Renovate Bot | e62713f42a | |
John Olheiser | c8c8e9758b | |
Renovate Bot | d15af88f83 | |
Renovate Bot | c74177556b | |
Renovate Bot | 02836d8e52 | |
Konstantin Podsvirov | b8ad55117a | |
Renovate Bot | fb4eb8be9c | |
Renovate Bot | 81b784d7a5 | |
Renovate Bot | 649b0b1272 | |
Renovate Bot | 3c9552c531 | |
techknowlogick | f28ddccd90 | |
Renovate Bot | 5beb5bf56b | |
Renovate Bot | 17e09a3c2d | |
Renovate Bot | 58962cbb65 | |
Renovate Bot | 7029e7e8c5 | |
Renovate Bot | 3e8047dfe6 | |
techknowlogick | 897dc50bfe | |
Renovate Bot | 2f1ca85b25 | |
Lunny Xiao | e6a6aa6f49 | |
Renovate Bot | bcda543bb8 | |
Sienna Lloyd | 9e5db56dcf | |
techknowlogick | 92d49e383d | |
techknowlogick | 2a666c84cb | |
techknowlogick | 32b9f5e836 | |
techknowlogick | 33442fd05f | |
Renovate Bot | f2cf5b7979 | |
techknowlogick | d80c8ceffd | |
Renovate Bot | e3b4d68af7 | |
techknowlogick | 0df93dd47a | |
jolheiser | 66b4decdd1 | |
techknowlogick | 9e1919ec0a | |
Renovate Bot | 615cd31eb2 | |
techknowlogick | b868d30434 | |
Renovate Bot | 2d57e07dc7 | |
Renovate Bot | 3343fedc17 | |
Renovate Bot | f9c67f35a6 | |
Renovate Bot | dd8d7fb30f | |
Renovate Bot | 17453fd7cb | |
Renovate Bot | fe4551142a | |
mvaldes14 | 10bd00d066 | |
jolheiser | 0869c15a6c | |
Martin Veldwijk | e4e8eb07d2 | |
John Olheiser | 620f236723 | |
Martin Veldwijk | 77837e909e | |
6543 | 5582f92df4 | |
vsysoev | c72c6c0679 | |
Grady Wong | b2d845b8c7 | |
Bo-Yi Wu | 07039b82e3 | |
appleboy | 7040c350ae | |
appleboy | b02263adb0 | |
appleboy | 4915862b95 | |
appleboy | cd6a7b35c3 | |
ASDFGamer | abcaa58312 | |
ASDFGamer | d9da952e96 | |
Lunny Xiao | 7f335a2b9c | |
6543 | b510756438 | |
techknowlogick | 3640bbac5e | |
techknowlogick | b705188599 | |
6543 | cfce25f0a7 | |
6543 | e783f4ea4a | |
John Olheiser | 342b573d45 | |
Lunny Xiao | 1a6d9b343a | |
techknowlogick | faa9858a7e | |
techknowlogick | e51eb1a00e | |
Wim | 6a848cb72a | |
Norwin | 2a8c1daa67 | |
6543 | 54b535a527 | |
6543 | c1c7870ceb | |
6543 | a37377d181 | |
6543 | 15457f1770 | |
Norwin | 4487213581 | |
Wim | 6a4ba6a689 | |
6543 | 4ee5ce4b52 | |
6543 | 6c9b2f8745 | |
Norwin | 1a256291dc | |
techknowlogick | 832136b6d4 | |
Norwin | 99e49991bb | |
Norwin | bbb287e29e | |
Norwin | 5e7c702e07 | |
Norwin | b8dbf899d2 | |
Norwin | 0b8be54186 | |
Norwin | 2b1bca9e5d | |
Norwin | d5a258213d | |
6543 | f83f579dea | |
6543 | 65535bd948 | |
Sandro | 02f5f15269 | |
Norwin | 883a27b14e | |
6543 | e54b32493d | |
Norwin | 329200b1ef | |
Norwin | 6663d9f19b | |
6543 | d06f35482e | |
Norwin | 9ab36c55fa | |
6543 | 40e606561f | |
Norwin | 0970b94552 | |
Norwin | dda94a5dea | |
Norwin | 16ba594a28 | |
Norwin | d8f4273ed0 | |
Norwin | 637e3f0666 | |
techknowlogick | ced24ccabb | |
arkamar | fb3e1f75e9 | |
Lunny Xiao | 0e24009fe9 | |
arkamar | cd24fd8e28 | |
Ikko Ashimine | dd300c1269 | |
kellya | 44c9e7e664 | |
Norwin | 268aa06179 | |
Norwin | a7d83ee416 | |
Norwin | a89f51f9ec | |
Norwin | d2295828d0 | |
Norwin | dc16643e0d | |
Norwin | ac25e89ebf | |
Norwin | 819cc1ab21 | |
Norwin | 78a95f1ca4 | |
Norwin | 5b77345b03 | |
Norwin | 23ce7b351d | |
Norwin | 375ece06d2 | |
techknowlogick | 4ffd994549 | |
Norwin | 58aaa17e7e | |
Norwin | 7a05be436c | |
justusbunsi | 3cf084cb96 | |
Norwin | 1e59dee685 | |
justusbunsi | 42e423470c | |
Norwin | 555f1ae516 | |
6543 | 1c690c5ff8 | |
Norwin | 802bdf7dc5 | |
Norwin | 1731e00ebd | |
Norwin | 7b7c7f57be | |
Norwin | 6e728cf812 | |
Norwin | 808e8b1c5a | |
Norwin | 9201250f74 | |
Norwin | 5b28a05eb7 | |
Norwin | 3fca309f2c | |
Norwin | d6df0a53b5 | |
6543 | 4b9907fb54 | |
6543 | ab4e11ae4d | |
nukuta | 546fcc16de | |
Norwin | 0f4f669cf0 | |
Norwin | 2bdd72dfff | |
techknowlogick | 64770a771f | |
6543 | ebb2c38a0a | |
Norwin | 616127cedc | |
plgruener | 3129e60a73 | |
Norwin | df724b4006 | |
Brahim HAMDOUNI | ffdbdb3d02 | |
Norwin | 568fde1ce5 | |
Norwin | 46945415c9 | |
Tamás Gérczei | 195bd2199c | |
Norwin | 0bf844018c | |
Norwin | 2319724bb2 | |
Norwin | 222d0501df | |
6543 | cb404b53b5 | |
Norwin | 3abc5a5b42 | |
Norwin | 6f738df4a5 | |
6543 | d22b314701 | |
6543 | 786c713ff5 | |
Norwin | d474883e90 | |
6543 | 0d98cbd657 | |
Norwin | 15c4edba1a | |
Norwin | e96cfdbbe7 | |
Norwin | 3c1efd33e2 | |
Norwin | 9c8321f2e0 | |
Norwin | b5c670ebf8 | |
Norwin | 95ef061711 | |
Norwin | 32b7b771cc | |
Norwin | 9efee7bf99 | |
Norwin | 8bb5c15745 | |
Norwin | 43a58bdba1 | |
Martin Reboredo | 43e9943757 | |
Norwin | 8b588f5313 | |
Martin Reboredo | a2e8b47c57 | |
Norwin | 83b73ce78e | |
Norwin | 782a6318f3 | |
Norwin | a948fd7e10 | |
Norwin | 287df8a715 | |
Norwin | dc67630b64 | |
Norwin | e5cdad554e | |
Martin Reboredo | b9f5ba0702 | |
6543 | 1b4487e6c9 | |
Norwin | b10d792687 | |
Norwin | 5a41c79d7d | |
Norwin | f5b0004a52 | |
6543 | c063329e9a | |
6543 | eeb9cbafe7 | |
Norwin | 0f38da068c | |
Norwin | 7e191eb18b | |
Norwin | a91168fd36 | |
Norwin | 4a11cf455f | |
Norwin | adb2382aa5 | |
khmarbaise | 6d6922efa6 | |
khmarbaise | 846fb3072a | |
techknowlogick | 3acd42f8d7 | |
6543 | c98441b13c | |
6543 | 5cb3e1ded5 | |
6543 | 2b11f408fd | |
Norwin | d0e05e8be2 | |
Norwin | 9a3b54b9a3 | |
khmarbaise | 16133212fc | |
khmarbaise | 16df81ac94 | |
khmarbaise | 7d486c2ec6 | |
khmarbaise | 4cb7d21a8f | |
6543 | 476900ab41 | |
Norwin | e6fbba3f80 | |
6543 | 0cea700dd8 | |
6543 | d5058b3b20 | |
Norwin | 355fd7aa53 | |
Norwin | 33468630e6 | |
6543 | 48c1c50796 | |
crapStone | a0330a3fb2 | |
Norwin | 6ea331ce3b | |
Norwin | a4b792e24d | |
crapStone | c4e2db32b5 | |
Norwin | cbd1bccbf9 | |
6543 | 136688997c | |
Norwin | 03ec6d0eee | |
Norwin | 30c3aa4f5b | |
6543 | 7ac3ffcc1b | |
6543 | e23f56e81c | |
Norwin | 3ee5501257 | |
6543 | f5dbd44ebe | |
Norwin | 3bfae84d32 | |
crapStone | c1d725ed34 | |
Norwin | de5a00e807 | |
6543 | f445ac7521 | |
6543 | 9602c149ca | |
6543 | cf2c18c32b | |
Norwin | 887495f38f | |
Norwin | 159bf03d49 | |
6543 | 288a8574c3 | |
6543 | 2e701ee8a2 | |
6543 | eacf1be066 | |
6543 | 3652f1dcb2 | |
Norwin | d7f429d246 | |
6543 | 89e93d90b3 | |
6543 | f8d983b523 | |
6543 | 7c30579900 | |
6543 | e4d7a77348 | |
6543 | 83b94ab864 | |
6543 | 3c1bcdb1e2 | |
6543 | f47ac8f96e | |
6543 | ed961c795e | |
6543 | e7ee745488 | |
Norwin | 9ae7196a50 | |
赵智超 | 25a7e85c1c | |
6543 | eb37f14923 | |
6543 | edd180a8f5 | |
6543 | a979df1070 | |
6543 | 08a9a9a8ab | |
6543 | 2135af0304 | |
6543 | 19ee32168e | |
6543 | 12ea1ad35c | |
6543 | 3a382e73b1 | |
6543 | f1801f39a6 | |
6543 | 66947bcf09 | |
6543 | 85e1244db8 | |
6543 | 618eeb9029 | |
techknowlogick | b2efb45f64 | |
6543 | 59fe58577a | |
6543 | 3c312cb409 | |
6543 | 6cff3b1cc7 | |
6543 | 4b059770ea | |
6543 | a35fcb682b | |
6543 | 8bbeeae327 | |
alrs | 35ad046d8d | |
6543 | e9dee07459 | |
6543 | 2653eaa6b2 | |
Norwin | 1a4d8edf37 | |
Norwin | 4cda7e0299 | |
6543 | 81fa4e78ff | |
6543 | 08a00add6d | |
Lunny Xiao | 7a10ea10df | |
6543 | b37673c954 | |
Norwin | f5d0790619 | |
6543 | 12f38c892f | |
6543 | 31a79f0605 | |
Norwin | 897e4ce3c1 | |
Lunny Xiao | 5601c57059 | |
6543 | c8cbd6b0ee | |
6543 | 8d61d8beec | |
appleboy | c20d7d45aa | |
6543 | 0a5cdd60ac | |
6543 | bffed6b4d3 | |
6543 | 3c66a7af24 | |
6543 | 6e85b47f7c | |
Norwin | d9e6db96a7 | |
Norwin | 97f0ea1c22 | |
techknowlogick | aa88745177 |
|
@ -0,0 +1,54 @@
|
||||||
|
# The full repository name
|
||||||
|
repo: gitea/tea
|
||||||
|
|
||||||
|
# Service type (gitea or github)
|
||||||
|
service: gitea
|
||||||
|
|
||||||
|
# Base URL for Gitea instance if using gitea service type (optional)
|
||||||
|
base-url: https://gitea.com
|
||||||
|
|
||||||
|
# Changelog groups and which labeled PRs to add to each group
|
||||||
|
groups:
|
||||||
|
-
|
||||||
|
name: BREAKING
|
||||||
|
labels:
|
||||||
|
- kind/breaking
|
||||||
|
-
|
||||||
|
name: FEATURES
|
||||||
|
labels:
|
||||||
|
- kind/feature
|
||||||
|
-
|
||||||
|
name: BUGFIXES
|
||||||
|
labels:
|
||||||
|
- kind/bug
|
||||||
|
-
|
||||||
|
name: ENHANCEMENTS
|
||||||
|
labels:
|
||||||
|
- kind/enhancement
|
||||||
|
-
|
||||||
|
name: SECURITY
|
||||||
|
labels:
|
||||||
|
- kind/security
|
||||||
|
-
|
||||||
|
name: TESTING
|
||||||
|
labels:
|
||||||
|
- kind/testing
|
||||||
|
-
|
||||||
|
name: TRANSLATION
|
||||||
|
labels:
|
||||||
|
- kind/translation
|
||||||
|
-
|
||||||
|
name: BUILD
|
||||||
|
labels:
|
||||||
|
- kind/build
|
||||||
|
- kind/lint
|
||||||
|
-
|
||||||
|
name: DOCS
|
||||||
|
labels:
|
||||||
|
- kind/docs
|
||||||
|
-
|
||||||
|
name: MISC
|
||||||
|
default: true
|
||||||
|
|
||||||
|
# regex indicating which labels to skip for the changelog
|
||||||
|
skip-labels: skip-changelog|backport\/.+
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "Tea DevContainer",
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/go:1.22-bullseye",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {}
|
||||||
|
},
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"settings": {},
|
||||||
|
"extensions": [
|
||||||
|
"editorconfig.editorconfig",
|
||||||
|
"golang.go",
|
||||||
|
"stylelint.vscode-stylelint",
|
||||||
|
"DavidAnson.vscode-markdownlint",
|
||||||
|
"ms-azuretools.vscode-docker",
|
||||||
|
"GitHub.vscode-pull-request-github"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
Dockerfile
|
||||||
|
tea
|
194
.drone.yml
194
.drone.yml
|
@ -1,194 +0,0 @@
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: default
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
workspace:
|
|
||||||
base: /go
|
|
||||||
path: src/code.gitea.io/tea
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build
|
|
||||||
pull: always
|
|
||||||
image: golang:1.13
|
|
||||||
environment:
|
|
||||||
GOPROXY: https://goproxy.cn
|
|
||||||
commands:
|
|
||||||
- make clean
|
|
||||||
- make vet
|
|
||||||
- make lint
|
|
||||||
- make fmt-check
|
|
||||||
- make misspell-check
|
|
||||||
- make test-vendor
|
|
||||||
- make build
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: unit-test
|
|
||||||
pull: always
|
|
||||||
image: golang:1.13
|
|
||||||
commands:
|
|
||||||
- make unit-test-coverage
|
|
||||||
settings:
|
|
||||||
group: test
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: release-test
|
|
||||||
pull: always
|
|
||||||
image: golang:1.13
|
|
||||||
commands:
|
|
||||||
- make test
|
|
||||||
settings:
|
|
||||||
group: test
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- "release/*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: tag-test
|
|
||||||
pull: always
|
|
||||||
image: golang:1.13
|
|
||||||
commands:
|
|
||||||
- make test
|
|
||||||
settings:
|
|
||||||
group: test
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: static
|
|
||||||
pull: always
|
|
||||||
image: techknowlogick/xgo:latest
|
|
||||||
commands:
|
|
||||||
- export PATH=$PATH:$GOPATH/bin
|
|
||||||
- make release
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: gpg-sign
|
|
||||||
pull: always
|
|
||||||
image: plugins/gpgsign:1
|
|
||||||
settings:
|
|
||||||
detach_sign: true
|
|
||||||
excludes:
|
|
||||||
- "dist/release/*.sha256"
|
|
||||||
files:
|
|
||||||
- "dist/release/*"
|
|
||||||
environment:
|
|
||||||
GPGSIGN_KEY:
|
|
||||||
from_secret: gpgsign_key
|
|
||||||
GPGSIGN_PASSPHRASE:
|
|
||||||
from_secret: gpgsign_passphrase
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: tag-release
|
|
||||||
pull: always
|
|
||||||
image: plugins/s3:1
|
|
||||||
settings:
|
|
||||||
acl: public-read
|
|
||||||
bucket: releases
|
|
||||||
endpoint: https://storage.gitea.io
|
|
||||||
path_style: true
|
|
||||||
source: "dist/release/*"
|
|
||||||
strip_prefix: dist/release/
|
|
||||||
target: "/tea/${DRONE_TAG##v}"
|
|
||||||
environment:
|
|
||||||
AWS_ACCESS_KEY_ID:
|
|
||||||
from_secret: aws_access_key_id
|
|
||||||
AWS_SECRET_ACCESS_KEY:
|
|
||||||
from_secret: aws_secret_access_key
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: release-branch-release
|
|
||||||
pull: always
|
|
||||||
image: plugins/s3:1
|
|
||||||
settings:
|
|
||||||
acl: public-read
|
|
||||||
bucket: releases
|
|
||||||
endpoint: https://storage.gitea.io
|
|
||||||
path_style: true
|
|
||||||
source: "dist/release/*"
|
|
||||||
strip_prefix: dist/release/
|
|
||||||
target: "/tea/${DRONE_BRANCH##release/v}"
|
|
||||||
environment:
|
|
||||||
AWS_ACCESS_KEY_ID:
|
|
||||||
from_secret: aws_access_key_id
|
|
||||||
AWS_SECRET_ACCESS_KEY:
|
|
||||||
from_secret: aws_secret_access_key
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- "release/*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: release
|
|
||||||
pull: always
|
|
||||||
image: plugins/s3:1
|
|
||||||
settings:
|
|
||||||
acl: public-read
|
|
||||||
bucket: releases
|
|
||||||
endpoint: https://storage.gitea.io
|
|
||||||
path_style: true
|
|
||||||
source: "dist/release/*"
|
|
||||||
strip_prefix: dist/release/
|
|
||||||
target: /tea/master
|
|
||||||
environment:
|
|
||||||
AWS_ACCESS_KEY_ID:
|
|
||||||
from_secret: aws_access_key_id
|
|
||||||
AWS_SECRET_ACCESS_KEY:
|
|
||||||
from_secret: aws_secret_access_key
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: gitea
|
|
||||||
pull: always
|
|
||||||
image: plugins/gitea-releases:1
|
|
||||||
settings:
|
|
||||||
files:
|
|
||||||
- "dist/release/*"
|
|
||||||
base_url: https://gitea.com
|
|
||||||
api_key:
|
|
||||||
from_secret: gitea_token
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: discord
|
|
||||||
pull: always
|
|
||||||
image: appleboy/drone-discord:1.0.0
|
|
||||||
environment:
|
|
||||||
DISCORD_WEBHOOK_ID:
|
|
||||||
from_secret: discord_webhook_id
|
|
||||||
DISCORD_WEBHOOK_TOKEN:
|
|
||||||
from_secret: discord_webhook_token
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
- pull_request
|
|
||||||
status:
|
|
||||||
- changed
|
|
||||||
- failure
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
name: "Bug Report"
|
||||||
|
about: "Use this template when reporting a bug, so you don't forget important information we'd ask for later."
|
||||||
|
title: "Bug: "
|
||||||
|
labels:
|
||||||
|
- kind/bug
|
||||||
|
---
|
||||||
|
|
||||||
|
### describe your environment
|
||||||
|
- tea version used (`tea -v`):
|
||||||
|
- [ ] I also reproduced the issue [with the latest 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)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
name: goreleaser
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- run: git fetch --force --tags
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: "go.mod"
|
||||||
|
- name: import gpg
|
||||||
|
id: import_gpg
|
||||||
|
uses: crazy-max/ghaction-import-gpg@v6
|
||||||
|
with:
|
||||||
|
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||||
|
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||||
|
- name: goreleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser-pro
|
||||||
|
version: "~> v1"
|
||||||
|
args: release --nightly
|
||||||
|
env:
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
AWS_REGION: ${{ secrets.AWS_REGION }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
S3_REGION: ${{ secrets.AWS_REGION }}
|
||||||
|
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
|
||||||
|
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
|
||||||
|
GPGSIGN_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
release-image:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKER_ORG: gitea
|
||||||
|
DOCKER_LATEST: nightly
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # all history for all branches and tags
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker BuildX
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
env:
|
||||||
|
ACTIONS_RUNTIME_TOKEN: '' # See https://gitea.com/gitea/act_runner/issues/119
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: |
|
||||||
|
linux/amd64
|
||||||
|
linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
gitea/tea:latest
|
|
@ -0,0 +1,42 @@
|
||||||
|
name: goreleaser
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- run: git fetch --force --tags
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: 'go.mod'
|
||||||
|
- name: import gpg
|
||||||
|
id: import_gpg
|
||||||
|
uses: crazy-max/ghaction-import-gpg@v6
|
||||||
|
with:
|
||||||
|
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||||
|
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||||
|
fingerprint: CC64B1DB67ABBEECAB24B6455FC346329753F4B0
|
||||||
|
- name: goreleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser-pro
|
||||||
|
version: "~> v1"
|
||||||
|
args: release
|
||||||
|
env:
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
AWS_REGION: ${{ secrets.AWS_REGION }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
S3_REGION: ${{ secrets.AWS_REGION }}
|
||||||
|
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
|
||||||
|
GORELEASER_FORCE_TOKEN: 'gitea'
|
||||||
|
GPGSIGN_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||||
|
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
|
||||||
|
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: check-and-test
|
||||||
|
|
||||||
|
on:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-and-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: 'go.mod'
|
||||||
|
- name: lint and build
|
||||||
|
run: |
|
||||||
|
make clean
|
||||||
|
make vet
|
||||||
|
make lint
|
||||||
|
make fmt-check
|
||||||
|
make misspell-check
|
||||||
|
make docs-check
|
||||||
|
make build
|
||||||
|
env:
|
||||||
|
GOPROXY: https://goproxy.io,direct
|
||||||
|
- name: test and coverage
|
||||||
|
run: |
|
||||||
|
make test
|
||||||
|
make unit-test-coverage
|
||||||
|
env:
|
||||||
|
GOPROXY: https://goproxy.io,direct
|
|
@ -1,4 +1,14 @@
|
||||||
tea
|
tea
|
||||||
|
/gitea-vet
|
||||||
|
/gitea-vet.exe
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
.history/
|
.history/
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
|
.vscode/
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
coverage.out
|
||||||
|
|
||||||
|
dist/
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,124 @@
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
- go mod tidy
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- darwin
|
||||||
|
- linux
|
||||||
|
- windows
|
||||||
|
- freebsd
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm
|
||||||
|
- arm64
|
||||||
|
goarm:
|
||||||
|
- "5"
|
||||||
|
- "6"
|
||||||
|
- "7"
|
||||||
|
ignore:
|
||||||
|
- goos: darwin
|
||||||
|
goarch: arm
|
||||||
|
- goos: darwin
|
||||||
|
goarch: ppc64le
|
||||||
|
- goos: darwin
|
||||||
|
goarch: s390x
|
||||||
|
- goos: windows
|
||||||
|
goarch: ppc64le
|
||||||
|
- goos: windows
|
||||||
|
goarch: s390x
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm
|
||||||
|
goarm: "5"
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm
|
||||||
|
goarm: "6"
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm
|
||||||
|
goarm: "7"
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm64
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: ppc64le
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: s390x
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm
|
||||||
|
goarm: "5"
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm
|
||||||
|
goarm: "6"
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm
|
||||||
|
goarm: "7"
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm64
|
||||||
|
flags:
|
||||||
|
- -trimpath
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X main.Version={{ .Version }}
|
||||||
|
binary: >-
|
||||||
|
{{ .ProjectName }}-
|
||||||
|
{{- .Version }}-
|
||||||
|
{{- .Os }}-
|
||||||
|
{{- if eq .Arch "amd64" }}amd64
|
||||||
|
{{- else if eq .Arch "amd64_v1" }}amd64
|
||||||
|
{{- else if eq .Arch "386" }}386
|
||||||
|
{{- else }}{{ .Arch }}{{ end }}
|
||||||
|
{{- if .Arm }}-{{ .Arm }}{{ end }}
|
||||||
|
no_unique_dist_dir: true
|
||||||
|
hooks:
|
||||||
|
post:
|
||||||
|
- cmd: xz -k -9 {{ .Path }}
|
||||||
|
dir: ./dist/
|
||||||
|
- cmd: sh .goreleaser.checksum.sh {{ .Path }}
|
||||||
|
- cmd: sh .goreleaser.checksum.sh {{ .Path }}.xz
|
||||||
|
|
||||||
|
blobs:
|
||||||
|
-
|
||||||
|
provider: s3
|
||||||
|
bucket: "{{ .Env.S3_BUCKET }}"
|
||||||
|
region: "{{ .Env.S3_REGION }}"
|
||||||
|
folder: "tea/{{.Version}}"
|
||||||
|
extra_files:
|
||||||
|
- glob: ./**.xz
|
||||||
|
- glob: ./**.sha256
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- format: binary
|
||||||
|
name_template: "{{ .Binary }}"
|
||||||
|
allow_different_binary_count: true
|
||||||
|
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
extra_files:
|
||||||
|
- glob: ./**.xz
|
||||||
|
|
||||||
|
force_token: gitea
|
||||||
|
|
||||||
|
signs:
|
||||||
|
-
|
||||||
|
signature: "${artifact}.sig"
|
||||||
|
artifacts: checksum
|
||||||
|
stdin: '{{ .Env.GPGSIGN_PASSPHRASE }}'
|
||||||
|
args: ["--batch", "-u", "{{ .Env.GPG_FINGERPRINT }}", "--output", "${signature}", "--detach-sign", "${artifact}"]
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ .Branch }}-devel"
|
||||||
|
|
||||||
|
nightly:
|
||||||
|
name_template: "{{ .Branch }}"
|
||||||
|
|
||||||
|
gitea_urls:
|
||||||
|
api: https://gitea.com/api/v1
|
||||||
|
download: https://gitea.com
|
||||||
|
|
||||||
|
release:
|
||||||
|
extra_files:
|
||||||
|
- glob: ./**.xz
|
||||||
|
- glob: ./**.xz.sha256
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
|
||||||
|
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
|
@ -16,7 +16,6 @@ warningCode = 1
|
||||||
[rule.increment-decrement]
|
[rule.increment-decrement]
|
||||||
[rule.var-naming]
|
[rule.var-naming]
|
||||||
[rule.var-declaration]
|
[rule.var-declaration]
|
||||||
[rule.package-comments]
|
|
||||||
[rule.range]
|
[rule.range]
|
||||||
[rule.receiver-naming]
|
[rule.receiver-naming]
|
||||||
[rule.time-naming]
|
[rule.time-naming]
|
||||||
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [v0.9.1](https://gitea.com/gitea/tea/releases/tag/v0.9.1) - 2023-02-15
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Print pull dont crash if it has TeamReviewRequests (#517)
|
||||||
|
|
||||||
|
## [v0.9.0](https://gitea.com/gitea/tea/releases/tag/v0.9.0) - 2022-09-13
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* Rename master branch to main (#495)
|
||||||
|
* Return RFC3339 UTC timestamps for machine-readable output (#470)
|
||||||
|
* FEATURES
|
||||||
|
* Allow editing multiline prompts with external text editor (#429)
|
||||||
|
* Add `tea admin user list` (#427)
|
||||||
|
* Add `tea whoami` command (#426)
|
||||||
|
* Add `tea org create <name>` (#420)
|
||||||
|
* Add `tea clone` (#411)
|
||||||
|
* Add `tea repo fork` (#410)
|
||||||
|
* Add `tea repo create-from-template` (#408)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fetch all items where needed. (#475)
|
||||||
|
* Fix running in repos without remote (#472)
|
||||||
|
* Add TSV to machine-readable formats (#467)
|
||||||
|
* Fix create milestone with deadline bug (#462)
|
||||||
|
* Fix resolving of URLs in markdown (#401)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Don't emit ANSI sequences when not emitting to TTY for markdown (#491)
|
||||||
|
* Show more version info (#486)
|
||||||
|
* Add preference `flag_defaults.remote`, refactor (#466)
|
||||||
|
* Add `--fields` to notification & milestone listings (#422)
|
||||||
|
* PR listing: add --fields & expose additional fields (#415)
|
||||||
|
* Add more flags to `tea repo create` (#409)
|
||||||
|
* Implement more issue filters (#400)
|
||||||
|
* MISC
|
||||||
|
* Simplify build & update installation instructions (#437)
|
||||||
|
* Clarify command descriptions when no arguments are taken (#496)
|
||||||
|
* Improve Documentation (#433)
|
||||||
|
* Use golang v1.18 and drop vendor folder (#478)
|
||||||
|
* Correct spelling of "wether" to "whether" in usage output (#453)
|
||||||
|
|
||||||
|
## [v0.8.0](https://gitea.com/gitea/tea/releases/tag/v0.8.0) - 2021-09-22
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* `tea notifications --all` has moved to `tea notifications --mine` (#389)
|
||||||
|
* `tea notifications` now only works with the context of a remote repo. (#389)
|
||||||
|
To run this outside of a local git dir, run either tea n `--mine` or `tea n --repo <my/repo>`
|
||||||
|
* FEATURES
|
||||||
|
* Add `tea pr merge` (#348)
|
||||||
|
* BUGFIXES
|
||||||
|
* Don't skip reading the local repo when `--repo` specifies a repo slug (#398)
|
||||||
|
* Fix adding login without token on private instances (#392)
|
||||||
|
* Correctly match login by ssh host with port (#391)
|
||||||
|
* Fix printing issue deadline (#388)
|
||||||
|
* Return useful error on wrong sshkey path (#374)
|
||||||
|
* Fix parsing of `--description` for issue/pr create (#371)
|
||||||
|
* Add missing flags (#369)
|
||||||
|
* Check negative limit command parameter (#358) (#359)
|
||||||
|
* Add missing flags to org & labels subcommands (#357)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Don't require a body for comment PR reviews (#399)
|
||||||
|
* Accept more main branch names for login detection (#396)
|
||||||
|
* Make local repo optional for `tea pr create`(#393)
|
||||||
|
* Notifications Add State Field (#384)
|
||||||
|
* Improve error messages (#370)
|
||||||
|
* Add tab completion for fish shell (#364)
|
||||||
|
* Text editor selection: follow unix defacto standards (#356)
|
||||||
|
* MISC
|
||||||
|
* Update Dependencies (#390)
|
||||||
|
|
||||||
|
## [v0.7.1](https://gitea.com/gitea/tea/releases/tag/v0.7.1) - 2021-08-27
|
||||||
|
|
||||||
|
* BUILD
|
||||||
|
* Enable release builds for darwin/arm64 (#360)
|
||||||
|
|
||||||
|
## [v0.7.0](https://gitea.com/gitea/tea/releases/tag/v0.7.0) - 2021-03-12
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* `tea issue create`: move `-b` flag to `-d` (#331)
|
||||||
|
* Drop `tea notif` shorthand in favor of `tea n` (#307)
|
||||||
|
* FEATURES
|
||||||
|
* Add commands for reviews (#315)
|
||||||
|
* Add `tea comment` and show comments of issues/pulls (#313)
|
||||||
|
* Add interactive mode for `tea milestone create` (#310)
|
||||||
|
* Add command to install shell completion (#309)
|
||||||
|
* Implement PR closing and reopening (#304)
|
||||||
|
* Add interactive mode for `tea issue create` (#302)
|
||||||
|
* BUGFIXES
|
||||||
|
* Introduce workaround for missing pull head sha (#340)
|
||||||
|
* Don't exit if we can't find a local repo with a remote matching to a login (#336)
|
||||||
|
* Don't push before creating a pull (#334)
|
||||||
|
* InitCommand() robustness (#327)
|
||||||
|
* `tea comment`: handle piped stdin (#322)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Allow checking out PRs with deleted head branch (#341)
|
||||||
|
* Markdown renderer: detect terminal width, resolve relative URLs (#332)
|
||||||
|
* Add more issue / pr creation parameters (#331)
|
||||||
|
* Improve `tea time` (#319)
|
||||||
|
* `tea pr checkout`: dont create local branches (#314)
|
||||||
|
* Add `tea issues --fields`, allow printing labels (#312)
|
||||||
|
* Add more command shorthands (#307)
|
||||||
|
* Show PR CI status (#306)
|
||||||
|
* Make PR workflow helpers more robust (#300)
|
||||||
|
|
||||||
|
## [v0.6.0](https://gitea.com/gitea/tea/releases/tag/v0.6.0) - 2020-12-11
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* Add `tea repos search`, improve repo listing (#215)
|
||||||
|
* Add Detail View for Login (#212)
|
||||||
|
* FEATURES
|
||||||
|
* Add interactive mode for `tea pr create` (#279)
|
||||||
|
* Add organization delete command (#270)
|
||||||
|
* Add organization list command (#264)
|
||||||
|
* BUGFIXES
|
||||||
|
* Forces needed arguments to `tea ms issues` (#297)
|
||||||
|
* Subcommands work outside of git repos (#285)
|
||||||
|
* Fix repo flag ignores local repo for login detection (#285)
|
||||||
|
* Improve ssh handling (#277)
|
||||||
|
* Issue create return web url (#257)
|
||||||
|
* Support prerelease gitea instances (#252)
|
||||||
|
* Fix `tea pr create` within same repo (#248)
|
||||||
|
* Handle login name case-insensitive on all comands (#227)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Add `tea login delete` (#296)
|
||||||
|
* Release delete: add --delete-tag & --confirm (#286)
|
||||||
|
* Sorted milestones list (#281)
|
||||||
|
* Pull clean & checkout use token for http(s) auth (#275)
|
||||||
|
* Show more infos in pull detail view (#271)
|
||||||
|
* Specify fields to print on `tea repos list` (#223)
|
||||||
|
* Print times in local timezone (#217)
|
||||||
|
* Issue create/edit print details (#214)
|
||||||
|
* Improve `tea logout` (#213)
|
||||||
|
* Added a shorthand for notifications (#209)
|
||||||
|
* Common subcommand naming scheme (#208)
|
||||||
|
* `tea pr checkout`: fetch via ssh if available (#192)
|
||||||
|
* Major refactor of codebase
|
||||||
|
* BUILD
|
||||||
|
* Use gox to cross-compile (#274)
|
||||||
|
* DOCS
|
||||||
|
* Update Docs to new code structure (#247)
|
||||||
|
|
||||||
|
## [v0.5.0](https://gitea.com/gitea/tea/releases/tag/v0.5.0) - 2020-09-27
|
||||||
|
|
||||||
|
* BREAKING
|
||||||
|
* Add Login Manage Functions (#182)
|
||||||
|
* FEATURES
|
||||||
|
* Add Release Subcomands (#195)
|
||||||
|
* Render Markdown and colorize labels table (#181)
|
||||||
|
* Add BasicAuth & Interactive for Login (#174)
|
||||||
|
* Add milestones subcomands (#149)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix Pulls Create (#202)
|
||||||
|
* Pulls create: detect head branch repo owner (#193)
|
||||||
|
* Fix Labels Delete (#180)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Add Pagination Options for List Subcomands (#204)
|
||||||
|
* Issues/Pulls: Details show State (#196)
|
||||||
|
* Make issues & pulls subcommands consistent (#188)
|
||||||
|
* Update SDK to v0.13.0 (#179)
|
||||||
|
* More Options To Specify Repo (#178)
|
||||||
|
* Add Repo Create subcomand & enhancements (#173)
|
||||||
|
* Times: format duration as seconds for machine-readable outputs (#168)
|
||||||
|
* Add user message to login list view (#166)
|
||||||
|
|
||||||
|
## [v0.4.1](https://gitea.com/gitea/tea/releases/tag/v0.4.1) - 2020-09-13
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Notification don't relay on a repo (#159)
|
||||||
|
|
||||||
|
## [v0.4.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1264) - 2020-07-18
|
||||||
|
|
||||||
|
* FEATURES
|
||||||
|
* Add notifications subcomand (#148)
|
||||||
|
* Add subcomand 'pulls create' (#144)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix Login Detection By Repo Param (#151)
|
||||||
|
* Fix Login List Output (#150)
|
||||||
|
* Fix --ssh-key Option (#135)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Subcomand Login Show List By Default (#152)
|
||||||
|
* BUILD
|
||||||
|
* Migrate src-d/go-git to go-git/go-git (#128)
|
||||||
|
* Migrate gitea-sdk to v0.12.0 (#133)
|
||||||
|
* Migrate yaml lib (#130)
|
||||||
|
* Add gitea-vet (#121)
|
||||||
|
|
||||||
|
## [v0.3.1](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1265) - 2020-06-15
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* --ssh-key should be string not bool (#135) (#137)
|
||||||
|
* modules/git: fix dropped error (#127)
|
||||||
|
* Issues details: add missing newline (#126)
|
||||||
|
|
||||||
|
## [v0.3.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=1227) - 2020-04-22
|
||||||
|
|
||||||
|
* FEATURES
|
||||||
|
* Add `tea pulls [checkout | clean]` commands (#93 #97 #107) (#105)
|
||||||
|
* Add `tea open` (#101)
|
||||||
|
* Add `tea issues [open|close]` commands (#99)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Ignore PRs for `tea issues` (#111)
|
||||||
|
* Add --state flag filter to issue & PR lists (#100)
|
||||||
|
|
||||||
|
## [v0.2.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=538) - 2020-03-06
|
||||||
|
* FEATURES
|
||||||
|
* Add `tea times` command (#54)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Upgrade urfave/cli to v2 version (#85)
|
||||||
|
* Add --remote flag to add/create subcommands (#77)
|
||||||
|
* BUILD
|
||||||
|
* Upgrade gitea/go-sdk to 2020-01-03 (#81)
|
||||||
|
* Update stretchr/testify v1.3.0 -> v1.4.0 (#83)
|
||||||
|
* Improve makefile to enable goproxy when go get tools (#98)
|
||||||
|
|
||||||
|
## [v0.1.2](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-11-15
|
||||||
|
* BUILD
|
||||||
|
* Fix typo in drone (#75)
|
||||||
|
|
||||||
|
## [v0.1.1](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-11-15
|
||||||
|
* FEATURES
|
||||||
|
* Add repos subcommand (#65)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Minor improvements to command-line language (#66)
|
||||||
|
|
||||||
|
## [v0.1.0](https://gitea.com/gitea/tea/pulls?q=&type=all&state=closed&milestone=59) - 2019-10-28
|
||||||
|
* BREAKING
|
||||||
|
* Changed git config determination to go-git (#41) [continue #45] (#62)
|
||||||
|
* FEATURES
|
||||||
|
* Add labels commands (#36)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix out -o flag (#53)
|
||||||
|
* Fix log formatting, refactor flag definition in cmd/labels.go (#52)
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* List label description (#60)
|
||||||
|
* Use Different Remote Repos (#58)
|
||||||
|
* Unified output (#14) (#40)
|
||||||
|
* Added global appendable Flags (#12) (#39)
|
||||||
|
* BUILD
|
||||||
|
* Change .drone.yml to new format (#33)
|
||||||
|
* DOCS
|
||||||
|
* Add install guide from brew on README (#61)
|
153
CONTRIBUTING.md
153
CONTRIBUTING.md
|
@ -7,8 +7,6 @@
|
||||||
- [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)
|
|
||||||
- [Translation](#translation)
|
|
||||||
- [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)
|
||||||
|
@ -20,36 +18,31 @@
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This document explains how to contribute changes to the Gitea project.
|
This document explains how to contribute changes to TEA.
|
||||||
It assumes you have followed the
|
|
||||||
[installation instructions](https://docs.gitea.io/en-us/).
|
|
||||||
Sensitive security-related issues should be reported to
|
Sensitive security-related issues should be reported to
|
||||||
[security@gitea.io](mailto:security@gitea.io).
|
[security@gitea.io](mailto:security@gitea.io).
|
||||||
|
|
||||||
For configuring IDE or code editor to develop Gitea see [IDE and code editor configuration](contrib/ide/)
|
For configuring IDE or code editor to develop Gitea see [IDE and code editor configuration](https://github.com/go-gitea/gitea/tree/master/contrib/ide)
|
||||||
|
|
||||||
## Bug reports
|
## Bug reports
|
||||||
|
|
||||||
Please search the issues on the issue tracker with a variety of keywords
|
Please search the issues on the issue tracker with a variety of keywords
|
||||||
to ensure your bug is not already reported.
|
to ensure your bug is not already reported.
|
||||||
|
|
||||||
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new)
|
If unique, [open an issue](https://gitea.com/gitea/tea/issues/new).
|
||||||
and answer the questions so we can understand and reproduce the
|
|
||||||
problematic behavior.
|
|
||||||
|
|
||||||
To show us that the issue you are having is in Gitea itself, please
|
Please write clear, concise instructions so we can reproduce the behavior—
|
||||||
write clear, concise instructions so we can reproduce the behavior—
|
|
||||||
even if it seems obvious. The more detailed and specific you are,
|
even if it seems obvious. The more detailed and specific you are,
|
||||||
the faster we can fix the issue. Check out [How to Report Bugs
|
the faster we can fix the issue. Check out [How to Report Bugs
|
||||||
Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
|
Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
|
||||||
|
|
||||||
Please be kind, remember that Gitea comes at no cost to you, and you're
|
Please be kind, remember that TEA comes at no cost to you, and you're
|
||||||
getting free help.
|
getting free help.
|
||||||
|
|
||||||
## Discuss your design
|
## Discuss your design
|
||||||
|
|
||||||
The project welcomes submissions. If you want to change or add something,
|
The project welcomes submissions. If you want to change or add something,
|
||||||
please let everyone know what you're working on—[file an issue](https://github.com/go-gitea/gitea/issues/new)!
|
please let everyone know what you're working on—[file an issue](https://gitea.com/gitea/tea/issues/new)!
|
||||||
Significant changes must go through the change proposal process
|
Significant changes must go through the change proposal process
|
||||||
before they can be accepted. To create a proposal, file an issue with
|
before they can be accepted. To create a proposal, file an issue with
|
||||||
your proposed changes documented, and make sure to note in the title
|
your proposed changes documented, and make sure to note in the title
|
||||||
|
@ -63,55 +56,14 @@ high-level discussions.
|
||||||
|
|
||||||
## Testing redux
|
## Testing redux
|
||||||
|
|
||||||
Before sending code out for review, run all the tests for the
|
Before sending code out for review, run all the test by executing: `make test`
|
||||||
whole tree to make sure the changes don't break other usage
|
Since TEA is an cli tool it should be obvious to test your feature locally first.
|
||||||
and keep the compatibility on upgrade. To make sure you are
|
|
||||||
running the test suite exactly like we do, you should install
|
|
||||||
the CLI for [Drone CI](https://github.com/drone/drone), as
|
|
||||||
we are using the server for continous testing, following [these
|
|
||||||
instructions](http://docs.drone.io/cli-installation/). After that,
|
|
||||||
you can simply call `drone exec --local --build-event "pull_request"` within
|
|
||||||
your working directory and it will try to run the test suite locally.
|
|
||||||
|
|
||||||
## Vendoring
|
|
||||||
|
|
||||||
We keep a cached copy of dependencies within the `vendor/` directory,
|
|
||||||
managing updates via [dep](https://github.com/golang/dep).
|
|
||||||
|
|
||||||
Pull requests should only include `vendor/` updates if they are part of
|
|
||||||
the same change, be it a bugfix or a feature addition.
|
|
||||||
|
|
||||||
The `vendor/` update needs to be justified as part of the PR description,
|
|
||||||
and must be verified by the reviewers and/or merger to always reference
|
|
||||||
an existing upstream commit.
|
|
||||||
|
|
||||||
You can find more information on how to get started with it on the [dep project website](https://golang.github.io/dep/docs/introduction.html).
|
|
||||||
|
|
||||||
## Translation
|
|
||||||
|
|
||||||
We do all translation work inside [Crowdin](https://crowdin.com/project/gitea).
|
|
||||||
The only translation that is maintained in this git repository is
|
|
||||||
[`en_US.ini`](https://github.com/go-gitea/gitea/blob/master/options/locale/locale_en-US.ini)
|
|
||||||
and is synced regularily to Crowdin. Once a translation has reached
|
|
||||||
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
|
|
||||||
included in the next released version.
|
|
||||||
|
|
||||||
## Building Gitea
|
|
||||||
|
|
||||||
Generally, the go build tools are installed as-needed in the `Makefile`.
|
|
||||||
An exception are the tools to build the CSS and images.
|
|
||||||
|
|
||||||
- To build CSS: Install [Node.js](https://nodejs.org/en/download/package-manager)
|
|
||||||
with `npm` and then run `npm install` and `make generate-stylesheets`.
|
|
||||||
- To build Images: ImageMagick, inkscape and zopflipng binaries must be
|
|
||||||
available in your `PATH` to run `make generate-images`.
|
|
||||||
|
|
||||||
## Code review
|
## Code review
|
||||||
|
|
||||||
Changes to Gitea 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 GitHub's
|
makes the change, even if they are an owner or a maintainer. We use Gitea's
|
||||||
pull request workflow to do that. And, we also use [LGTM](http://lgtm.co)
|
pull request & review workflow to do that. Gitea ensure every PR is reviewed by at least 2 maintainers.
|
||||||
to ensure every PR is reviewed by at least 2 maintainers.
|
|
||||||
|
|
||||||
Please try to make your pull request easy to review for us. And, please read
|
Please try to make your pull request easy to review for us. And, please read
|
||||||
the *[How to get faster PR reviews](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews)* guide;
|
the *[How to get faster PR reviews](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews)* guide;
|
||||||
|
@ -128,6 +80,41 @@ Some of the key points:
|
||||||
|
|
||||||
## Styleguide
|
## Styleguide
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
- Subcommands should follow the following structure:
|
||||||
|
```
|
||||||
|
tea <noun> <verb> [<noun>] [<flags>]
|
||||||
|
```
|
||||||
|
|
||||||
|
for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
tea issues list
|
||||||
|
tea pulls create
|
||||||
|
tea teams add user --team x --user y
|
||||||
|
```
|
||||||
|
- Commands should accept nouns as singular & plural by making use of the `Aliases` field.
|
||||||
|
- The default action without a verb is `list`.
|
||||||
|
- There is a standard set of verbs: `list/ls`, `create`, `edit`, `delete`
|
||||||
|
- `ls` lists objects with filter options, and applies pagination where available.
|
||||||
|
- `delete` should show info what is deleted and ask user again, if force flag`-y` is not set
|
||||||
|
- Verbs that accept large numbers of flags provide an interactive mode when called without any arguments or flags.
|
||||||
|
- Try to reuse as many flag definitions as possible, see `cmd/flags/flags.go`.
|
||||||
|
- Always make sure that the help texts are properly set, and as concise as possible.
|
||||||
|
|
||||||
|
### Internal Module Structure
|
||||||
|
- `cmd`: only contains command/flag options for `urfave/cli`
|
||||||
|
- subcommands are in a subpackage named after its parent command
|
||||||
|
- `modules/task`: contain func for doing something with gitea
|
||||||
|
(e.g. create token by user/passwd)
|
||||||
|
- `modules/print`: contain all functions that print to stdout
|
||||||
|
- `modules/config`: config tea & login things
|
||||||
|
- `modules/interact`: contain functions to interact with user by prompts
|
||||||
|
- `modules/git`: do git related stuff (get info/push/pull/checkout/...)
|
||||||
|
- `modules/utils`: helper functions used by other functions
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
Use `make fmt`, check with `make lint`.
|
||||||
For imports you should use the following format (_without_ the comments)
|
For imports you should use the following format (_without_ the comments)
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
|
@ -164,25 +151,7 @@ commit automatically with `git commit -s`.
|
||||||
|
|
||||||
## Release Cycle
|
## Release Cycle
|
||||||
|
|
||||||
We adopted a release schedule to streamline the process of working
|
Before we reach v1 there is no fixed release cycle.
|
||||||
on, finishing, and issuing releases. The overall goal is to make a
|
|
||||||
minor release every two months, which breaks down into one month of
|
|
||||||
general development followed by one month of testing and polishing
|
|
||||||
known as the release freeze. All the feature pull requests should be
|
|
||||||
merged in the first month of one release period. And, during the frozen
|
|
||||||
period, a corresponding release branch is open for fixes backported from
|
|
||||||
master. Release candidates are made during this period for user testing to
|
|
||||||
obtain a final version that is maintained in this branch. A release is
|
|
||||||
maintained by issuing patch releases to only correct critical problems
|
|
||||||
such as crashes or security issues.
|
|
||||||
|
|
||||||
Major release cycles are bimonthly. They always begin on the 25th and end on
|
|
||||||
the 24th (i.e., the 25th of December to February 24th).
|
|
||||||
|
|
||||||
During a development cycle, we may also publish any necessary minor releases
|
|
||||||
for the previous version. For example, if the latest, published release is
|
|
||||||
v1.2, then minor changes for the previous release—e.g., v1.1.0 -> v1.1.1—are
|
|
||||||
still possible.
|
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
|
|
||||||
|
@ -191,7 +160,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/NsatcWJ) #develop channel. The owners
|
[Discord](https://discord.gg/Gitea) #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
|
||||||
|
@ -208,6 +177,9 @@ https://help.github.com/articles/signing-commits-with-gpg/
|
||||||
|
|
||||||
## Owners
|
## Owners
|
||||||
|
|
||||||
|
This repo is part of the Gitea project and as such part of that project's
|
||||||
|
governance.
|
||||||
|
|
||||||
Since Gitea is a pure community organization without any company support,
|
Since Gitea is a pure community organization without any company support,
|
||||||
to keep the development healthy we will elect three owners every year. All
|
to keep the development healthy we will elect three owners every year. All
|
||||||
contributors may vote to elect up to three candidates, one of which will
|
contributors may vote to elect up to three candidates, one of which will
|
||||||
|
@ -221,7 +193,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/NsatcWJ) #general channel. Below are the
|
[Discord](https://discord.gg/Gitea) #general channel. Below are the
|
||||||
words to speak:
|
words to speak:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -230,22 +202,9 @@ I'm honored to having been elected an owner of Gitea, I agree with
|
||||||
and lead the development of Gitea.
|
and lead the development of Gitea.
|
||||||
```
|
```
|
||||||
|
|
||||||
To honor the past owners, here's the history of the owners and the time
|
|
||||||
they served:
|
|
||||||
|
|
||||||
* 2016-11-04 ~ 2017-12-31
|
|
||||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
|
||||||
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
|
||||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
|
||||||
|
|
||||||
* 2018-01-01 ~ 2018-12-31
|
|
||||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
|
||||||
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
|
||||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
|
||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
Gitea has the `master` branch as a tip branch and has version branches
|
tea has the `master` branch as a tip branch and has version branches
|
||||||
such as `release/v0.9`. `release/v0.9` is a release branch and we will
|
such as `release/v0.9`. `release/v0.9` is a release branch and we will
|
||||||
tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept
|
tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept
|
||||||
pull requests on the `release/v0.9` branch and publish a `v0.9.1` tag,
|
pull requests on the `release/v0.9` branch and publish a `v0.9.1` tag,
|
||||||
|
@ -261,9 +220,9 @@ be reviewed by two maintainers and must pass the automatic tests.
|
||||||
Code that you contribute should use the standard copyright header:
|
Code that you contribute should use the standard copyright header:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// 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
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
FROM cgr.dev/chainguard/go:latest AS build
|
||||||
|
COPY . /build/
|
||||||
|
WORKDIR /build
|
||||||
|
RUN make build
|
||||||
|
|
||||||
|
FROM cgr.dev/chainguard/static:latest
|
||||||
|
COPY --from=build /build/tea /tea
|
||||||
|
VOLUME [ "/app" ]
|
||||||
|
ENV HOME="/app"
|
||||||
|
ENTRYPOINT ["/tea"]
|
|
@ -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
|
1
LICENSE
1
LICENSE
|
@ -1,5 +1,4 @@
|
||||||
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
|
||||||
|
|
161
Makefile
161
Makefile
|
@ -1,31 +1,12 @@
|
||||||
DIST := dist
|
DIST := dist
|
||||||
IMPORT := code.gitea.io/tea
|
|
||||||
export GO111MODULE=off
|
|
||||||
|
|
||||||
GO ?= go
|
GO ?= go
|
||||||
SED_INPLACE := sed -i
|
|
||||||
SHASUM ?= shasum -a 256
|
SHASUM ?= shasum -a 256
|
||||||
|
|
||||||
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
|
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
|
||||||
|
|
||||||
ifeq ($(OS), Windows_NT)
|
GOFILES := $(shell find . -name "*.go" -type f ! -path "*/bindata.go")
|
||||||
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)
|
||||||
|
@ -33,27 +14,31 @@ else
|
||||||
ifneq ($(DRONE_BRANCH),)
|
ifneq ($(DRONE_BRANCH),)
|
||||||
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||||
else
|
else
|
||||||
VERSION ?= master
|
VERSION ?= main
|
||||||
endif
|
endif
|
||||||
TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
||||||
endif
|
endif
|
||||||
|
TEA_VERSION_TAG ?= $(shell sed 's/+/_/' <<< $(TEA_VERSION))
|
||||||
LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)"
|
|
||||||
|
|
||||||
PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/)
|
|
||||||
SOURCES ?= $(shell find . -name "*.go" -type f)
|
|
||||||
|
|
||||||
TAGS ?=
|
TAGS ?=
|
||||||
|
SDK ?= $(shell $(GO) list -f '{{.Version}}' -m code.gitea.io/sdk/gitea)
|
||||||
|
LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)" -X "main.SDK=$(SDK)" -s -w
|
||||||
|
|
||||||
|
# override to allow passing additional goflags via make CLI
|
||||||
|
override GOFLAGS := $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)'
|
||||||
|
|
||||||
|
PACKAGES ?= $(shell $(GO) list ./...)
|
||||||
|
SOURCES ?= $(shell find . -name "*.go" -type f)
|
||||||
|
|
||||||
|
# OS specific vars.
|
||||||
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
|
||||||
|
|
||||||
|
@ -68,28 +53,23 @@ fmt:
|
||||||
|
|
||||||
.PHONY: vet
|
.PHONY: vet
|
||||||
vet:
|
vet:
|
||||||
|
# Default vet
|
||||||
$(GO) vet $(PACKAGES)
|
$(GO) vet $(PACKAGES)
|
||||||
|
# Custom vet
|
||||||
|
$(GO) build code.gitea.io/gitea-vet
|
||||||
|
$(GO) vet -vettool=$(VET_TOOL) $(PACKAGES)
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint: install-lint-tools
|
||||||
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
$(GO) run github.com/mgechev/revive@v1.3.2 -config .revive.toml ./... || exit 1
|
||||||
$(GO) get -u github.com/mgechev/revive; \
|
|
||||||
fi
|
|
||||||
revive -config .revive.toml -exclude=./vendor/... ./... || exit 1
|
|
||||||
|
|
||||||
.PHONY: misspell-check
|
.PHONY: misspell-check
|
||||||
misspell-check:
|
misspell-check: install-lint-tools
|
||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
$(GO) run github.com/client9/misspell/cmd/misspell@latest -error -i unknwon,destory $(GOFILES)
|
||||||
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
|
||||||
fi
|
|
||||||
misspell -error -i unknwon,destory $(GOFILES)
|
|
||||||
|
|
||||||
.PHONY: misspell
|
.PHONY: misspell
|
||||||
misspell:
|
misspell: install-lint-tools
|
||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
$(GO) run github.com/client9/misspell/cmd/misspell@latest -w -i unknwon $(GOFILES)
|
||||||
$(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:
|
||||||
|
@ -101,88 +81,53 @@ fmt-check:
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
|
.PHONY: docs
|
||||||
|
docs:
|
||||||
|
$(GO) run . docs --out docs/CLI.md
|
||||||
|
|
||||||
|
.PHONY: docs-check
|
||||||
|
docs-check:
|
||||||
|
@DIFF=$$($(GO) run . docs | diff docs/CLI.md -); \
|
||||||
|
if [ -n "$$DIFF" ]; then \
|
||||||
|
echo "Please run 'make docs' and commit the result:"; \
|
||||||
|
echo "$$DIFF"; \
|
||||||
|
exit 1; \
|
||||||
|
fi;
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
GO111MODULE=on $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' $(PACKAGES)
|
$(GO) test -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 -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||||
|
|
||||||
.PHONY: vendor
|
.PHONY: tidy
|
||||||
vendor:
|
tidy:
|
||||||
GO111MODULE=on $(GO) mod tidy && GO111MODULE=on $(GO) mod vendor
|
$(GO) mod tidy
|
||||||
|
|
||||||
.PHONY: test-vendor
|
|
||||||
test-vendor: vendor
|
|
||||||
@diff=$$(git diff vendor/); \
|
|
||||||
if [ -n "$$diff" ]; then \
|
|
||||||
echo "Please run 'make vendor' and commit the result:"; \
|
|
||||||
echo "$${diff}"; \
|
|
||||||
exit 1; \
|
|
||||||
fi;
|
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: test
|
check: test
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install: $(wildcard *.go)
|
install: $(SOURCES)
|
||||||
$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
@echo "installing to $(shell $(GO) env GOPATH)/bin/$(EXECUTABLE)"
|
||||||
|
$(GO) install -v $(BUILDMODE) $(GOFLAGS)
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: $(EXECUTABLE)
|
build: $(EXECUTABLE)
|
||||||
|
|
||||||
$(EXECUTABLE): $(SOURCES)
|
$(EXECUTABLE): $(SOURCES)
|
||||||
GO111MODULE=on $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
$(GO) build $(BUILDMODE) $(GOFLAGS) -o $@
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: build-image
|
||||||
release: release-dirs release-windows release-linux release-darwin release-copy release-compress release-check
|
build-image:
|
||||||
|
docker build --build-arg VERSION=$(TEA_VERSION) -t gitea/tea:$(TEA_VERSION_TAG) .
|
||||||
|
|
||||||
.PHONY: release-dirs
|
install-lint-tools:
|
||||||
release-dirs:
|
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
mkdir -p $(DIST)/binaries $(DIST)/release
|
$(GO) install github.com/mgechev/revive@v1.3.2; \
|
||||||
|
|
||||||
.PHONY: release-windows
|
|
||||||
release-windows:
|
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
|
||||||
fi
|
fi
|
||||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out tea-$(VERSION) .
|
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
ifeq ($(CI),drone)
|
$(GO) install github.com/client9/misspell/cmd/misspell@latest; \
|
||||||
cp /build/* $(DIST)/binaries
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: release-linux
|
|
||||||
release-linux:
|
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
|
||||||
fi
|
fi
|
||||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out tea-$(VERSION) .
|
|
||||||
ifeq ($(CI),drone)
|
|
||||||
cp /build/* $(DIST)/binaries
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: release-darwin
|
|
||||||
release-darwin:
|
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
|
||||||
fi
|
|
||||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out tea-$(VERSION) .
|
|
||||||
ifeq ($(CI),drone)
|
|
||||||
cp /build/* $(DIST)/binaries
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: release-copy
|
|
||||||
release-copy:
|
|
||||||
cd $(DIST); for file in `find /build -type f -name "*"`; do cp $${file} ./release/; done;
|
|
||||||
|
|
||||||
.PHONY: release-compress
|
|
||||||
release-compress:
|
|
||||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
$(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
|
||||||
fi
|
|
||||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
|
||||||
|
|
||||||
.PHONY: release-check
|
|
||||||
release-check:
|
|
||||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
|
|
||||||
|
|
152
README.md
152
README.md
|
@ -1,67 +1,137 @@
|
||||||
# Gitea Command Line Tool for Go
|
# <img alt='tea logo' src='https://gitea.com/repo-avatars/550-80a3a8c2ab0e2c2d69f296b7f8582485' height="40"/> *T E A*
|
||||||
|
|
||||||
This project acts as a command line tool for operating one or multiple Gitea instances. It depends on [code.gitea.io/sdk](https://code.gitea.io/sdk) client SDK implementation written in Go to interact with
|
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
|
||||||
the Gitea API implementation.
|
[![Release](https://raster.shields.io/badge/dynamic/json.svg?label=release&url=https://gitea.com/api/v1/repos/gitea/tea/releases&query=$[0].tag_name)](https://gitea.com/gitea/tea/releases)
|
||||||
|
[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/tea)](https://goreportcard.com/report/code.gitea.io/tea) [![GoDoc](https://pkg.go.dev/badge/code.gitea.io/tea?status.svg)](https://godoc.org/code.gitea.io/tea)
|
||||||
|
![Tea Release Status](https://gitea.com/gitea/tea/actions/workflows/release-nightly.yml/badge.svg)
|
||||||
|
|
||||||
|
## The official CLI for Gitea
|
||||||
|
|
||||||
|
![demo gif](./demo.gif)
|
||||||
|
|
||||||
|
```
|
||||||
|
tea - command line tool to interact with Gitea
|
||||||
|
version 0.8.0-preview
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
tea command [subcommand] [command options] [arguments...]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
tea is a productivity helper for Gitea. It can be used to manage most entities on
|
||||||
|
one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.
|
||||||
|
|
||||||
|
tea tries to make use of context provided by the repository in $PWD if available.
|
||||||
|
tea works best in a upstream/fork workflow, when the local main branch tracks the
|
||||||
|
upstream repo. tea assumes that local git state is published on the remote before
|
||||||
|
doing operations with tea. Configuration is persisted in $XDG_CONFIG_HOME/tea.
|
||||||
|
|
||||||
|
COMMANDS
|
||||||
|
help, h Shows a list of commands or help for one command
|
||||||
|
ENTITIES:
|
||||||
|
issues, issue, i List, create and update issues
|
||||||
|
pulls, pull, pr Manage and checkout pull requests
|
||||||
|
labels, label Manage issue labels
|
||||||
|
milestones, milestone, ms List and create milestones
|
||||||
|
releases, release, r Manage releases
|
||||||
|
release assets, release asset, r a Manage release attachments
|
||||||
|
times, time, t Operate on tracked times of a repository's issues & pulls
|
||||||
|
organizations, organization, org List, create, delete organizations
|
||||||
|
repos, repo Show repository details
|
||||||
|
comment, c Add a comment to an issue / pr
|
||||||
|
HELPERS:
|
||||||
|
open, o Open something of the repository in web browser
|
||||||
|
notifications, notification, n Show notifications
|
||||||
|
clone, C Clone a repository locally
|
||||||
|
SETUP:
|
||||||
|
logins, login Log in to a Gitea server
|
||||||
|
logout Log out from a Gitea server
|
||||||
|
shellcompletion, autocomplete Install shell completion for tea
|
||||||
|
whoami Show current logged in user
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
--help, -h show help (default: false)
|
||||||
|
--version, -v print the version (default: false)
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
tea login add # add a login once to get started
|
||||||
|
|
||||||
|
tea pulls # list open pulls for the repo in $PWD
|
||||||
|
tea pulls --repo $HOME/foo # list open pulls for the repo in $HOME/foo
|
||||||
|
tea pulls --remote upstream # list open pulls for the repo pointed at by
|
||||||
|
# your local "upstream" git remote
|
||||||
|
# list open pulls for any gitea repo at the given login instance
|
||||||
|
tea pulls --repo gitea/tea --login gitea.com
|
||||||
|
|
||||||
|
tea milestone issues 0.7.0 # view open issues for milestone '0.7.0'
|
||||||
|
tea issue 189 # view contents of issue 189
|
||||||
|
tea open 189 # open web ui for issue 189
|
||||||
|
tea open milestones # open web ui for milestones
|
||||||
|
|
||||||
|
# send gitea desktop notifications every 5 minutes (bash + libnotify)
|
||||||
|
while :; do tea notifications --mine -o simple | xargs -i notify-send {}; sleep 300; done
|
||||||
|
|
||||||
|
ABOUT
|
||||||
|
Written & maintained by The Gitea Authors.
|
||||||
|
If you find a bug or want to contribute, we'll welcome you at https://gitea.com/gitea/tea.
|
||||||
|
More info about Gitea itself on https://about.gitea.com.
|
||||||
|
```
|
||||||
|
|
||||||
|
- [Compare features with other git forge CLIs](./FEATURE-COMPARISON.md)
|
||||||
|
- tea uses [code.gitea.io/sdk](https://code.gitea.io/sdk) and interacts with the Gitea API.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Currently no prebuilt binaries are provided.
|
There are different ways to get `tea`:
|
||||||
To install, a Go installation is needed.
|
|
||||||
|
|
||||||
|
1. Install via your system package manager:
|
||||||
|
- macOS via `brew` (official):
|
||||||
```sh
|
```sh
|
||||||
go get code.gitea.io/tea
|
brew install tea
|
||||||
go install code.gitea.io/tea
|
|
||||||
```
|
```
|
||||||
|
- arch linux ([gitea-tea-git](https://aur.archlinux.org/packages/gitea-tea-git), thirdparty)
|
||||||
|
- alpine linux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge), thirdparty)
|
||||||
|
- Windows via `MSYS2` ([tea](https://packages.msys2.org/base/mingw-w64-tea), thirdparty)
|
||||||
|
|
||||||
If the `tea` executable is not found, you might need to set up your `$GOPATH` and `$PATH` variables first:
|
2. Use the prebuilt binaries from [dl.gitea.com](https://dl.gitea.com/tea/)
|
||||||
|
|
||||||
```sh
|
3. Install from source: [see *Compilation*](#compilation)
|
||||||
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have `brew` installed, you can install tea version via:
|
4. Docker (thirdparty): [tgerczei/tea](https://hub.docker.com/r/tgerczei/tea)
|
||||||
|
|
||||||
```sh
|
5. asdf (thirdparty): [mvaldes14/asdf-tea](https://github.com/mvaldes14/asdf-tea)
|
||||||
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
|
|
||||||
brew install --devel tea
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
First of all, you have to create a token on your `personal settings -> application` page of your gitea instance.
|
|
||||||
Use this token to login with `tea`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
tea login add --name=try --url=https://try.gitea.io --token=xxxxxx
|
|
||||||
```
|
|
||||||
|
|
||||||
Now you can use the `tea` commands:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
tea issues
|
|
||||||
tea releases
|
|
||||||
```
|
|
||||||
|
|
||||||
> If you are inside a git repository hosted on a gitea instance, you don't need to specify the `--login` and `--repo` flags!
|
|
||||||
|
|
||||||
## Compilation
|
## Compilation
|
||||||
|
|
||||||
To compile the sources yourself run the following:
|
Make sure you have a current go version installed (1.13 or newer).
|
||||||
|
|
||||||
|
- To compile the source yourself with the recommended flags & tags:
|
||||||
```sh
|
```sh
|
||||||
go get code.gitea.io/tea
|
git clone https://gitea.com/gitea/tea.git # or: tea clone gitea.com/gitea/tea ;)
|
||||||
cd "${GOPATH}/src/code.gitea.io/tea"
|
cd tea
|
||||||
go build
|
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
|
||||||
|
|
||||||
Fork -> Patch -> Push -> Pull Request
|
Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
## Authors
|
- `make test` run testsuite
|
||||||
|
- `make vet` run checks (check the order of imports; preventing failure on CI pipeline beforehand)
|
||||||
|
- ... (for other development tasks, check the `Makefile`)
|
||||||
|
|
||||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
**Please** read the [CONTRIBUTING](CONTRIBUTING.md) documentation, it will tell you about internal structures and concepts.
|
||||||
* [Contributors](https://github.com/go-gitea/tea/graphs/contributors)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
//go:build vendor
|
||||||
|
// +build vendor
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Libraries that are included to vendor utilities used during build.
|
||||||
|
// These libraries will not be included in a normal compilation.
|
||||||
|
|
||||||
|
import (
|
||||||
|
// for vet
|
||||||
|
_ "code.gitea.io/gitea-vet"
|
||||||
|
)
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/admin/users"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdAdmin represents the namespace of admin commands.
|
||||||
|
// The command itself has no functionality, but hosts subcommands.
|
||||||
|
var CmdAdmin = cli.Command{
|
||||||
|
Name: "admin",
|
||||||
|
Usage: "Operations requiring admin access on the Gitea instance",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Category: catMisc,
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
return cli.ShowSubcommandHelp(cmd)
|
||||||
|
},
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&cmdAdminUsers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdAdminUsers = cli.Command{
|
||||||
|
Name: "users",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Usage: "Manage registered users",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
if ctx.Args().Len() == 1 {
|
||||||
|
return runAdminUserDetail(ctx, ctx.Args().First())
|
||||||
|
}
|
||||||
|
return users.RunUserList(ctx)
|
||||||
|
},
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&users.CmdUserList,
|
||||||
|
},
|
||||||
|
Flags: users.CmdUserList.Flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAdminUserDetail(cmd *cli.Context, u string) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
user, _, err := client.GetUserInfo(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.UserDetails(user)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var userFieldsFlag = flags.FieldsFlag(print.UserFields, []string{
|
||||||
|
"id", "login", "full_name", "email", "activated",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdUserList represents a sub command of users to list users
|
||||||
|
var CmdUserList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List Users",
|
||||||
|
Description: "List users",
|
||||||
|
Action: RunUserList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
userFieldsFlag,
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunUserList list users
|
||||||
|
func RunUserList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
fields, err := userFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
users, _, err := client.AdminListUsers(gitea.AdminListUsersOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.UserList(users, ctx.Output, fields)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/attachments"
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdReleaseAttachments represents a release attachment (file attachment)
|
||||||
|
var CmdReleaseAttachments = cli.Command{
|
||||||
|
Name: "assets",
|
||||||
|
Aliases: []string{"asset", "a"},
|
||||||
|
Category: catEntities,
|
||||||
|
Usage: "Manage release assets",
|
||||||
|
Description: "Manage release assets",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: attachments.RunReleaseAttachmentList,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&attachments.CmdReleaseAttachmentList,
|
||||||
|
&attachments.CmdReleaseAttachmentCreate,
|
||||||
|
&attachments.CmdReleaseAttachmentDelete,
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package attachments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdReleaseAttachmentCreate represents a sub command of Release Attachments to create a release attachment
|
||||||
|
var CmdReleaseAttachmentCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create one or more release attachments",
|
||||||
|
Description: `Create one or more release attachments`,
|
||||||
|
ArgsUsage: "<release-tag> <asset> [<asset>...]",
|
||||||
|
Action: runReleaseAttachmentCreate,
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runReleaseAttachmentCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
if ctx.Args().Len() < 2 {
|
||||||
|
return fmt.Errorf("No release tag or assets specified.\nUsage:\t%s", ctx.Command.UsageText)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := ctx.Args().First()
|
||||||
|
if len(tag) == 0 {
|
||||||
|
return fmt.Errorf("Release tag needed to create attachment")
|
||||||
|
}
|
||||||
|
|
||||||
|
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, asset := range ctx.Args().Slice()[1:] {
|
||||||
|
var file *os.File
|
||||||
|
if file, err = os.Open(asset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := filepath.Base(asset)
|
||||||
|
|
||||||
|
if _, _, err = ctx.Login.Client().CreateReleaseAttachment(ctx.Owner, ctx.Repo, release.ID, file, filePath); err != nil {
|
||||||
|
file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package attachments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdReleaseAttachmentDelete represents a sub command of Release Attachments to delete a release attachment
|
||||||
|
var CmdReleaseAttachmentDelete = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "Delete one or more release attachments",
|
||||||
|
Description: `Delete one or more release attachments`,
|
||||||
|
ArgsUsage: "<release tag> <attachment name> [<attachment name>...]",
|
||||||
|
Action: runReleaseAttachmentDelete,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "confirm",
|
||||||
|
Aliases: []string{"y"},
|
||||||
|
Usage: "Confirm deletion (required)",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runReleaseAttachmentDelete(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
if ctx.Args().Len() < 2 {
|
||||||
|
return fmt.Errorf("No release tag or attachment names specified.\nUsage:\t%s", ctx.Command.UsageText)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := ctx.Args().First()
|
||||||
|
if len(tag) == 0 {
|
||||||
|
return fmt.Errorf("Release tag needed to delete attachment")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ctx.Bool("confirm") {
|
||||||
|
fmt.Println("Are you sure? Please confirm with -y or --confirm.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
existing, _, err := client.ListReleaseAttachments(ctx.Owner, ctx.Repo, release.ID, gitea.ListReleaseAttachmentsOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range ctx.Args().Slice()[1:] {
|
||||||
|
var attachment *gitea.Attachment
|
||||||
|
for _, a := range existing {
|
||||||
|
if a.Name == name {
|
||||||
|
attachment = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if attachment == nil {
|
||||||
|
return fmt.Errorf("Release does not have attachment named '%s'", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.DeleteReleaseAttachment(ctx.Owner, ctx.Repo, release.ID, attachment.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReleaseAttachmentByName(owner, repo string, release int64, name string, client *gitea.Client) (*gitea.Attachment, error) {
|
||||||
|
al, _, err := client.ListReleaseAttachments(owner, repo, release, gitea.ListReleaseAttachmentsOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(al) == 0 {
|
||||||
|
return nil, fmt.Errorf("Release does not have any attachments")
|
||||||
|
}
|
||||||
|
for _, a := range al {
|
||||||
|
if a.Name == name {
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Attachment does not exist")
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package attachments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdReleaseAttachmentList represents a sub command of release attachment to list release attachments
|
||||||
|
var CmdReleaseAttachmentList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List Release Attachments",
|
||||||
|
Description: "List Release Attachments",
|
||||||
|
ArgsUsage: "<release-tag>", // command does not accept arguments
|
||||||
|
Action: RunReleaseAttachmentList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunReleaseAttachmentList list release attachments
|
||||||
|
func RunReleaseAttachmentList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
tag := ctx.Args().First()
|
||||||
|
if len(tag) == 0 {
|
||||||
|
return fmt.Errorf("Release tag needed to list attachments")
|
||||||
|
}
|
||||||
|
|
||||||
|
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attachments, _, err := ctx.Login.Client().ListReleaseAttachments(ctx.Owner, ctx.Repo, release.ID, gitea.ListReleaseAttachmentsOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.ReleaseAttachmentsList(attachments, ctx.Output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) {
|
||||||
|
rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rl) == 0 {
|
||||||
|
return nil, fmt.Errorf("Repo does not have any release")
|
||||||
|
}
|
||||||
|
for _, r := range rl {
|
||||||
|
if r.TagName == tag {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Release tag does not exist")
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/adrg/xdg"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdAutocomplete manages autocompletion
|
||||||
|
var CmdAutocomplete = cli.Command{
|
||||||
|
Name: "shellcompletion",
|
||||||
|
Aliases: []string{"autocomplete"},
|
||||||
|
Category: catSetup,
|
||||||
|
Usage: "Install shell completion for tea",
|
||||||
|
Description: "Install shell completion for tea",
|
||||||
|
ArgsUsage: "<shell type> (bash, zsh, powershell, fish)",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "install",
|
||||||
|
Usage: "Persist in shell config instead of printing commands",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: runAutocompleteAdd,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAutocompleteAdd(ctx *cli.Context) error {
|
||||||
|
var remoteFile, localFile, cmds string
|
||||||
|
shell := ctx.Args().First()
|
||||||
|
|
||||||
|
switch shell {
|
||||||
|
case "zsh":
|
||||||
|
remoteFile = "contrib/autocomplete.zsh"
|
||||||
|
localFile = "autocomplete.zsh"
|
||||||
|
cmds = "echo 'PROG=tea _CLI_ZSH_AUTOCOMPLETE_HACK=1 source \"%s\"' >> ~/.zshrc && source ~/.zshrc"
|
||||||
|
|
||||||
|
case "bash":
|
||||||
|
remoteFile = "contrib/autocomplete.sh"
|
||||||
|
localFile = "autocomplete.sh"
|
||||||
|
cmds = "echo 'PROG=tea source \"%s\"' >> ~/.bashrc && source ~/.bashrc"
|
||||||
|
|
||||||
|
case "powershell":
|
||||||
|
remoteFile = "contrib/autocomplete.ps1"
|
||||||
|
localFile = "tea.ps1"
|
||||||
|
cmds = "\"& %s\" >> $profile"
|
||||||
|
|
||||||
|
case "fish":
|
||||||
|
// fish is different, in that urfave/cli provides a generator for the shell script needed.
|
||||||
|
// this also means that the fish completion can become out of sync with the tea binary!
|
||||||
|
// writing to this directory suffices, as fish reads files there on startup, no cmds needed.
|
||||||
|
return writeFishAutoCompleteFile(ctx)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Must specify valid %s", ctx.Command.ArgsUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
localPath, err := xdg.ConfigFile("tea/" + localFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds = fmt.Sprintf(cmds, localPath)
|
||||||
|
if err = writeRemoteAutoCompleteFile(remoteFile, localPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Bool("install") {
|
||||||
|
fmt.Println("Installing in your shellrc")
|
||||||
|
installer := exec.Command(shell, "-c", cmds)
|
||||||
|
if shell == "powershell" {
|
||||||
|
installer = exec.Command("powershell.exe", "-Command", cmds)
|
||||||
|
}
|
||||||
|
out, err := installer.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Couldn't run the commands: %s %s", err, out)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("\n# Run the following commands to install autocompletion (or use --install)")
|
||||||
|
fmt.Println(cmds)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeRemoteAutoCompleteFile(file, destPath string) error {
|
||||||
|
url := fmt.Sprintf("https://gitea.com/gitea/tea/raw/branch/master/%s", file)
|
||||||
|
fmt.Println("Fetching " + url)
|
||||||
|
|
||||||
|
res, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
writer, err := os.Create(destPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer writer.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(writer, res.Body)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFishAutoCompleteFile(ctx *cli.Context) error {
|
||||||
|
// NOTE: to make sure this file is in sync with tea commands, we'd need to
|
||||||
|
// - check if the file exists
|
||||||
|
// - if it does, check if the tea version that wrote it is the currently running version
|
||||||
|
// - if not, rewrite the file
|
||||||
|
// on each application run
|
||||||
|
// NOTE: this generates a completion that also suggests file names, which looks kinda messy..
|
||||||
|
script, err := ctx.App.ToFishCompletion()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
localPath, err := xdg.ConfigFile("fish/conf.d/tea_completion.fish")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
writer, err := os.Create(localPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = io.WriteString(writer, script); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Installed tab completion to %s\n", localPath)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/branches"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdBranches represents to login a gitea server.
|
||||||
|
var CmdBranches = cli.Command{
|
||||||
|
Name: "branches",
|
||||||
|
Aliases: []string{"branch", "b"},
|
||||||
|
Category: catEntities,
|
||||||
|
Usage: "Consult branches",
|
||||||
|
Description: `Lists branches when called without argument. If a branch is provided, will show it in detail.`,
|
||||||
|
ArgsUsage: "[<branch name>]",
|
||||||
|
Action: runBranches,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&branches.CmdBranchesList,
|
||||||
|
&branches.CmdBranchesProtect,
|
||||||
|
&branches.CmdBranchesUnprotect,
|
||||||
|
},
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "comments",
|
||||||
|
Usage: "Whether to display comments (will prompt if not provided & run interactively)",
|
||||||
|
},
|
||||||
|
}, branches.CmdBranchesList.Flags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runBranches(ctx *cli.Context) error {
|
||||||
|
return branches.RunBranchesList(ctx)
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package branches
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var branchFieldsFlag = flags.FieldsFlag(print.BranchFields, []string{
|
||||||
|
"name", "protected", "user-can-merge", "user-can-push",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdBranchesListFlags Flags for command list
|
||||||
|
var CmdBranchesListFlags = append([]cli.Flag{
|
||||||
|
branchFieldsFlag,
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...)
|
||||||
|
|
||||||
|
// CmdBranchesList represents a sub command of branches to list branches
|
||||||
|
var CmdBranchesList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List branches of the repository",
|
||||||
|
Description: `List branches of the repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunBranchesList,
|
||||||
|
Flags: CmdBranchesListFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunBranchesList list branches
|
||||||
|
func RunBranchesList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
owner := ctx.Owner
|
||||||
|
if ctx.IsSet("owner") {
|
||||||
|
owner = ctx.String("owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
var branches []*gitea.Branch
|
||||||
|
var protections []*gitea.BranchProtection
|
||||||
|
var err error
|
||||||
|
branches, _, err = ctx.Login.Client().ListRepoBranches(owner, ctx.Repo, gitea.ListRepoBranchesOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
protections, _, err = ctx.Login.Client().ListBranchProtections(owner, ctx.Repo, gitea.ListBranchProtectionsOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := branchFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.BranchesList(branches, protections, ctx.Output, fields)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package branches
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdBranchesProtectFlags Flags for command protect/unprotect
|
||||||
|
var CmdBranchesProtectFlags = append([]cli.Flag{
|
||||||
|
branchFieldsFlag,
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...)
|
||||||
|
|
||||||
|
// CmdBranchesProtect represents a sub command of branches to protect a branch
|
||||||
|
var CmdBranchesProtect = cli.Command{
|
||||||
|
Name: "protect",
|
||||||
|
Aliases: []string{"P"},
|
||||||
|
Usage: "Protect branches",
|
||||||
|
Description: `Block actions push/merge on specified branches`,
|
||||||
|
ArgsUsage: "<branch>",
|
||||||
|
Action: RunBranchesProtect,
|
||||||
|
Flags: CmdBranchesProtectFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdBranchesUnprotect represents a sub command of branches to protect a branch
|
||||||
|
var CmdBranchesUnprotect = cli.Command{
|
||||||
|
Name: "unprotect",
|
||||||
|
Aliases: []string{"U"},
|
||||||
|
Usage: "Unprotect branches",
|
||||||
|
Description: `Suppress existing protections on specified branches`,
|
||||||
|
ArgsUsage: "<branch>",
|
||||||
|
Action: RunBranchesProtect,
|
||||||
|
Flags: CmdBranchesProtectFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunBranchesProtect function to protect/unprotect a list of branches
|
||||||
|
func RunBranchesProtect(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
if !cmd.Args().Present() {
|
||||||
|
return fmt.Errorf("must specify at least one branch")
|
||||||
|
}
|
||||||
|
|
||||||
|
owner := ctx.Owner
|
||||||
|
if ctx.IsSet("owner") {
|
||||||
|
owner = ctx.String("owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, branch := range ctx.Args().Slice() {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
command := ctx.Command.Name
|
||||||
|
if command == "protect" {
|
||||||
|
_, _, err = ctx.Login.Client().CreateBranchProtection(owner, ctx.Repo, gitea.CreateBranchProtectionOption{
|
||||||
|
BranchName: branch,
|
||||||
|
RuleName: "",
|
||||||
|
EnablePush: false,
|
||||||
|
EnablePushWhitelist: false,
|
||||||
|
PushWhitelistUsernames: []string{},
|
||||||
|
PushWhitelistTeams: []string{},
|
||||||
|
PushWhitelistDeployKeys: false,
|
||||||
|
EnableMergeWhitelist: false,
|
||||||
|
MergeWhitelistUsernames: []string{},
|
||||||
|
MergeWhitelistTeams: []string{},
|
||||||
|
EnableStatusCheck: false,
|
||||||
|
StatusCheckContexts: []string{},
|
||||||
|
RequiredApprovals: 1,
|
||||||
|
EnableApprovalsWhitelist: false,
|
||||||
|
ApprovalsWhitelistUsernames: []string{},
|
||||||
|
ApprovalsWhitelistTeams: []string{},
|
||||||
|
BlockOnRejectedReviews: false,
|
||||||
|
BlockOnOfficialReviewRequests: false,
|
||||||
|
BlockOnOutdatedBranch: false,
|
||||||
|
DismissStaleApprovals: false,
|
||||||
|
RequireSignedCommits: false,
|
||||||
|
ProtectedFilePatterns: "",
|
||||||
|
UnprotectedFilePatterns: "",
|
||||||
|
})
|
||||||
|
} else if command == "unprotect" {
|
||||||
|
_, err = ctx.Login.Client().DeleteBranchProtection(owner, ctx.Repo, branch)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("command %s is not supported", command)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
var (
|
||||||
|
catSetup = "SETUP"
|
||||||
|
catEntities = "ENTITIES"
|
||||||
|
catHelpers = "HELPERS"
|
||||||
|
catMisc = "MISCELLANEOUS"
|
||||||
|
)
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/git"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoClone represents a sub command of repos to create a local copy
|
||||||
|
var CmdRepoClone = cli.Command{
|
||||||
|
Name: "clone",
|
||||||
|
Aliases: []string{"C"},
|
||||||
|
Usage: "Clone a repository locally",
|
||||||
|
Description: `Clone a repository locally, without a local git installation required.
|
||||||
|
The repo slug can be specified in different formats:
|
||||||
|
gitea/tea
|
||||||
|
tea
|
||||||
|
gitea.com/gitea/tea
|
||||||
|
git@gitea.com:gitea/tea
|
||||||
|
https://gitea.com/gitea/tea
|
||||||
|
ssh://gitea.com:22/gitea/tea
|
||||||
|
When a host is specified in the repo-slug, it will override the login specified with --login.
|
||||||
|
`,
|
||||||
|
Category: catHelpers,
|
||||||
|
Action: runRepoClone,
|
||||||
|
ArgsUsage: "<repo-slug> [target dir]",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "depth",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Usage: "num commits to fetch, defaults to all",
|
||||||
|
},
|
||||||
|
&flags.LoginFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoClone(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
args := ctx.Args()
|
||||||
|
if args.Len() < 1 {
|
||||||
|
return cli.ShowCommandHelp(cmd, "clone")
|
||||||
|
}
|
||||||
|
dir := args.Get(1)
|
||||||
|
|
||||||
|
var (
|
||||||
|
login *config.Login = ctx.Login
|
||||||
|
owner string = ctx.Login.User
|
||||||
|
repo string
|
||||||
|
)
|
||||||
|
|
||||||
|
// parse first arg as repo specifier
|
||||||
|
repoSlug := args.Get(0)
|
||||||
|
url, err := git.ParseURL(repoSlug)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
owner, repo = utils.GetOwnerAndRepo(url.Path, login.User)
|
||||||
|
if url.Host != "" {
|
||||||
|
login = config.GetLoginByHost(url.Host)
|
||||||
|
if login == nil {
|
||||||
|
return fmt.Errorf("No login configured matching host '%s', run `tea login add` first", url.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = task.RepoClone(
|
||||||
|
dir,
|
||||||
|
login,
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
interact.PromptPassword,
|
||||||
|
ctx.Int("depth"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdAddComment is the main command to operate with notifications
|
||||||
|
var CmdAddComment = cli.Command{
|
||||||
|
Name: "comment",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Category: catEntities,
|
||||||
|
Usage: "Add a comment to an issue / pr",
|
||||||
|
Description: "Add a comment to an issue / pr",
|
||||||
|
ArgsUsage: "<issue / pr index> [<comment body>]",
|
||||||
|
Action: runAddComment,
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAddComment(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
args := ctx.Args()
|
||||||
|
if args.Len() == 0 {
|
||||||
|
return fmt.Errorf("Please specify issue / pr index")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body := strings.Join(ctx.Args().Tail(), " ")
|
||||||
|
if interact.IsStdinPiped() {
|
||||||
|
// custom solution until https://github.com/AlecAivazis/survey/issues/328 is fixed
|
||||||
|
if bodyStdin, err := io.ReadAll(ctx.App.Reader); err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(bodyStdin) != 0 {
|
||||||
|
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
|
||||||
|
}
|
||||||
|
} else if len(body) == 0 {
|
||||||
|
if err = survey.AskOne(interact.NewMultiline(interact.Multiline{
|
||||||
|
Message: "Comment:",
|
||||||
|
Syntax: "md",
|
||||||
|
UseEditor: config.GetPreferences().Editor,
|
||||||
|
}), &body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(body) == 0 {
|
||||||
|
return fmt.Errorf("No comment body provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
comment, _, err := client.CreateIssueComment(ctx.Owner, ctx.Repo, idx, gitea.CreateIssueCommentOption{
|
||||||
|
Body: body,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.Comment(comment)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
246
cmd/config.go
246
cmd/config.go
|
@ -1,246 +0,0 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/http/cookiejar"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
|
||||||
local_git "code.gitea.io/tea/modules/git"
|
|
||||||
"code.gitea.io/tea/modules/utils"
|
|
||||||
go_git "gopkg.in/src-d/go-git.v4"
|
|
||||||
|
|
||||||
"github.com/go-gitea/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Login represents a login to a gitea server, you even could add multiple logins for one gitea server
|
|
||||||
type Login struct {
|
|
||||||
Name string `yaml:"name"`
|
|
||||||
URL string `yaml:"url"`
|
|
||||||
Token string `yaml:"token"`
|
|
||||||
Active bool `yaml:"active"`
|
|
||||||
SSHHost string `yaml:"ssh_host"`
|
|
||||||
Insecure bool `yaml:"insecure"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client returns a client to operate Gitea API
|
|
||||||
func (l *Login) Client() *gitea.Client {
|
|
||||||
client := gitea.NewClient(l.URL, l.Token)
|
|
||||||
if l.Insecure {
|
|
||||||
cookieJar, _ := cookiejar.New(nil)
|
|
||||||
|
|
||||||
client.SetHTTPClient(&http.Client{
|
|
||||||
Jar: cookieJar,
|
|
||||||
Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSSHHost returns SSH host name
|
|
||||||
func (l *Login) GetSSHHost() string {
|
|
||||||
if l.SSHHost != "" {
|
|
||||||
return l.SSHHost
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := url.Parse(l.URL)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return u.Hostname()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config reprensents local configurations
|
|
||||||
type Config struct {
|
|
||||||
Logins []Login `yaml:"logins"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
config Config
|
|
||||||
yamlConfigPath string
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
homeDir, err := utils.Home()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Retrieve home dir failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := filepath.Join(homeDir, ".tea")
|
|
||||||
err = os.MkdirAll(dir, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Init tea config dir " + dir + " failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
yamlConfigPath = filepath.Join(dir, "tea.yml")
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitRepo(repoPath string) (string, string) {
|
|
||||||
p := strings.Split(repoPath, "/")
|
|
||||||
if len(p) >= 2 {
|
|
||||||
return p[0], p[1]
|
|
||||||
}
|
|
||||||
return repoPath, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func getActiveLogin() (*Login, error) {
|
|
||||||
if len(config.Logins) == 0 {
|
|
||||||
return nil, errors.New("No available login")
|
|
||||||
}
|
|
||||||
for _, l := range config.Logins {
|
|
||||||
if l.Active {
|
|
||||||
return &l, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &config.Logins[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLoginByName(name string) *Login {
|
|
||||||
for _, l := range config.Logins {
|
|
||||||
if l.Name == name {
|
|
||||||
return &l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addLogin(login Login) error {
|
|
||||||
for _, l := range config.Logins {
|
|
||||||
if l.Name == login.Name {
|
|
||||||
if l.URL == login.URL && l.Token == login.Token {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors.New("Login name has already been used")
|
|
||||||
}
|
|
||||||
if l.URL == login.URL && l.Token == login.Token {
|
|
||||||
return errors.New("URL has been added")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := url.Parse(login.URL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if login.SSHHost == "" {
|
|
||||||
login.SSHHost = u.Hostname()
|
|
||||||
}
|
|
||||||
config.Logins = append(config.Logins, login)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isFileExist(fileName string) (bool, error) {
|
|
||||||
f, err := os.Stat(fileName)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if f.IsDir() {
|
|
||||||
return false, errors.New("A directory with the same name exists")
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadConfig(ymlPath string) error {
|
|
||||||
exist, _ := isFileExist(ymlPath)
|
|
||||||
if exist {
|
|
||||||
Println("Found config file", ymlPath)
|
|
||||||
bs, err := ioutil.ReadFile(ymlPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = yaml.Unmarshal(bs, &config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveConfig(ymlPath string) error {
|
|
||||||
bs, err := yaml.Marshal(&config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ioutil.WriteFile(ymlPath, bs, 0660)
|
|
||||||
}
|
|
||||||
|
|
||||||
func curGitRepoPath() (*Login, string, error) {
|
|
||||||
gitPath, err := go_git.PlainOpenWithOptions("./", &go_git.PlainOpenOptions{DetectDotGit: true})
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", errors.New("No Gitea login found")
|
|
||||||
}
|
|
||||||
gitConfig, err := gitPath.Config()
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no remote
|
|
||||||
if len(gitConfig.Remotes) == 0 {
|
|
||||||
return nil, "", errors.New("No remote(s) found in this Git repository")
|
|
||||||
}
|
|
||||||
|
|
||||||
// if only one remote exists
|
|
||||||
if len(gitConfig.Remotes) >= 1 && len(remoteValue) == 0 {
|
|
||||||
for remote := range gitConfig.Remotes {
|
|
||||||
remoteValue = remote
|
|
||||||
}
|
|
||||||
if len(gitConfig.Remotes) > 1 {
|
|
||||||
// if master branch is present, use it as the default remote
|
|
||||||
masterBranch, ok := gitConfig.Branches["master"]
|
|
||||||
if ok {
|
|
||||||
if len(masterBranch.Remote) > 0 {
|
|
||||||
remoteValue = masterBranch.Remote
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteConfig, ok := gitConfig.Remotes[remoteValue]
|
|
||||||
if !ok || remoteConfig == nil {
|
|
||||||
return nil, "", errors.New("Remote " + remoteValue + " not found in this Git repository")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, l := range config.Logins {
|
|
||||||
for _, u := range remoteConfig.URLs {
|
|
||||||
p, err := local_git.ParseURL(strings.TrimSpace(u))
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", fmt.Errorf("Git remote URL parse failed: %s", err.Error())
|
|
||||||
}
|
|
||||||
if strings.EqualFold(p.Scheme, "http") || strings.EqualFold(p.Scheme, "https") {
|
|
||||||
if strings.HasPrefix(u, l.URL) {
|
|
||||||
ps := strings.Split(p.Path, "/")
|
|
||||||
path := strings.Join(ps[len(ps)-2:], "/")
|
|
||||||
return &l, strings.TrimSuffix(path, ".git"), nil
|
|
||||||
}
|
|
||||||
} else if strings.EqualFold(p.Scheme, "ssh") {
|
|
||||||
if l.GetSSHHost() == strings.Split(p.Host, ":")[0] {
|
|
||||||
return &l, strings.TrimLeft(strings.TrimSuffix(p.Path, ".git"), "/"), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, "", errors.New("No Gitea login found. You might want to specify --repo (and --login) to work outside of a repository")
|
|
||||||
}
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdDocs generates markdown for tea
|
||||||
|
var CmdDocs = cli.Command{
|
||||||
|
Name: "docs",
|
||||||
|
Hidden: true,
|
||||||
|
Description: "Generate CLI docs",
|
||||||
|
Action: runDocs,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "out",
|
||||||
|
Usage: "Path to output docs to, otherwise prints to stdout",
|
||||||
|
Aliases: []string{"o"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDocs(ctx *cli.Context) error {
|
||||||
|
md, err := ctx.App.ToMarkdown()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
outPath := ctx.String("out")
|
||||||
|
if outPath == "" {
|
||||||
|
fmt.Print(md)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(outPath), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Create(outPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fi.Close()
|
||||||
|
if _, err := fi.WriteString(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
124
cmd/flags.go
124
cmd/flags.go
|
@ -1,124 +0,0 @@
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// create global variables for global Flags to simplify
|
|
||||||
// access to the options without requiring cli.Context
|
|
||||||
var (
|
|
||||||
loginValue string
|
|
||||||
repoValue string
|
|
||||||
outputValue string
|
|
||||||
remoteValue string
|
|
||||||
)
|
|
||||||
|
|
||||||
// LoginFlag provides flag to specify tea login profile
|
|
||||||
var LoginFlag = cli.StringFlag{
|
|
||||||
Name: "login, l",
|
|
||||||
Usage: "Use a different Gitea login. Optional",
|
|
||||||
Destination: &loginValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
// RepoFlag provides flag to specify repository
|
|
||||||
var RepoFlag = cli.StringFlag{
|
|
||||||
Name: "repo, r",
|
|
||||||
Usage: "Repository to interact with. Optional",
|
|
||||||
Destination: &repoValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoteFlag provides flag to specify remote repository
|
|
||||||
var RemoteFlag = cli.StringFlag{
|
|
||||||
Name: "remote, R",
|
|
||||||
Usage: "Discover Gitea login from remote. Optional",
|
|
||||||
Destination: &remoteValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputFlag provides flag to specify output type
|
|
||||||
var OutputFlag = cli.StringFlag{
|
|
||||||
Name: "output, o",
|
|
||||||
Usage: "Output format. (csv, simple, table, tsv, yaml)",
|
|
||||||
Destination: &outputValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoginOutputFlags defines login and output flags that should
|
|
||||||
// added to all subcommands and appended to the flags of the
|
|
||||||
// subcommand to work around issue and provide --login and --output:
|
|
||||||
// https://github.com/urfave/cli/issues/585
|
|
||||||
var LoginOutputFlags = []cli.Flag{
|
|
||||||
LoginFlag,
|
|
||||||
OutputFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoginRepoFlags defines login and repo flags that should
|
|
||||||
// be used for all subcommands and appended to the flags of
|
|
||||||
// the subcommand to work around issue and provide --login and --repo:
|
|
||||||
// https://github.com/urfave/cli/issues/585
|
|
||||||
var LoginRepoFlags = []cli.Flag{
|
|
||||||
LoginFlag,
|
|
||||||
RepoFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllDefaultFlags defines flags that should be available
|
|
||||||
// for all subcommands working with dedicated repositories
|
|
||||||
// to work around issue and provide --login, --repo and --output:
|
|
||||||
// https://github.com/urfave/cli/issues/585
|
|
||||||
var AllDefaultFlags = append([]cli.Flag{
|
|
||||||
RepoFlag,
|
|
||||||
RemoteFlag,
|
|
||||||
}, LoginOutputFlags...)
|
|
||||||
|
|
||||||
// initCommand returns repository and *Login based on flags
|
|
||||||
func initCommand() (*Login, string, string) {
|
|
||||||
err := loadConfig(yamlConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to load config file " + yamlConfigPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
var login *Login
|
|
||||||
if loginValue == "" {
|
|
||||||
login, err = getActiveLogin()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
login = getLoginByName(loginValue)
|
|
||||||
if login == nil {
|
|
||||||
log.Fatal("Login name " + loginValue + " does not exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repoPath := repoValue
|
|
||||||
if repoPath == "" {
|
|
||||||
login, repoPath, err = curGitRepoPath()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
owner, repo := splitRepo(repoPath)
|
|
||||||
return login, owner, repo
|
|
||||||
}
|
|
||||||
|
|
||||||
// initCommandLoginOnly return *Login based on flags
|
|
||||||
func initCommandLoginOnly() *Login {
|
|
||||||
err := loadConfig(yamlConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("load config file failed ", yamlConfigPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
var login *Login
|
|
||||||
|
|
||||||
login = getLoginByName(loginValue)
|
|
||||||
if login == nil {
|
|
||||||
log.Fatal("indicated login name ", loginValue, " does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
return login
|
|
||||||
}
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CsvFlag is a wrapper around cli.StringFlag, with an added GetValues() method
|
||||||
|
// to retrieve comma separated string values as a slice.
|
||||||
|
type CsvFlag struct {
|
||||||
|
cli.StringFlag
|
||||||
|
AvailableFields []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCsvFlag creates a CsvFlag, while setting its usage string and default values
|
||||||
|
func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag {
|
||||||
|
var availableDesc string
|
||||||
|
if len(availableValues) != 0 {
|
||||||
|
availableDesc = " Available values:"
|
||||||
|
}
|
||||||
|
return &CsvFlag{
|
||||||
|
AvailableFields: availableValues,
|
||||||
|
StringFlag: cli.StringFlag{
|
||||||
|
Name: name,
|
||||||
|
Aliases: aliases,
|
||||||
|
Value: strings.Join(defaults, ","),
|
||||||
|
Usage: fmt.Sprintf(`Comma-separated list of %s.%s
|
||||||
|
%s
|
||||||
|
`, usage, availableDesc, strings.Join(availableValues, ",")),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValues returns the value of the flag, parsed as a commaseparated list
|
||||||
|
func (f CsvFlag) GetValues(ctx *cli.Context) ([]string, error) {
|
||||||
|
val := ctx.String(f.Name)
|
||||||
|
selection := strings.Split(val, ",")
|
||||||
|
if f.AvailableFields != nil && val != "" {
|
||||||
|
for _, field := range selection {
|
||||||
|
if !utils.Contains(f.AvailableFields, field) {
|
||||||
|
return nil, fmt.Errorf("Invalid field '%s'", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selection, nil
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoginFlag provides flag to specify tea login profile
|
||||||
|
var LoginFlag = cli.StringFlag{
|
||||||
|
Name: "login",
|
||||||
|
Aliases: []string{"l"},
|
||||||
|
Usage: "Use a different Gitea Login. Optional",
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoFlag provides flag to specify repository
|
||||||
|
var RepoFlag = cli.StringFlag{
|
||||||
|
Name: "repo",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "Override local repository path or gitea repository slug to interact with. Optional",
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteFlag provides flag to specify remote repository
|
||||||
|
var RemoteFlag = cli.StringFlag{
|
||||||
|
Name: "remote",
|
||||||
|
Aliases: []string{"R"},
|
||||||
|
Usage: "Discover Gitea login from remote. Optional",
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutputFlag provides flag to specify output type
|
||||||
|
var OutputFlag = cli.StringFlag{
|
||||||
|
Name: "output",
|
||||||
|
Aliases: []string{"o"},
|
||||||
|
Usage: "Output format. (simple, table, csv, tsv, yaml, json)",
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaginationPageFlag provides flag for pagination options
|
||||||
|
var PaginationPageFlag = cli.StringFlag{
|
||||||
|
Name: "page",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "specify page, default is 1",
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaginationLimitFlag provides flag for pagination options
|
||||||
|
var PaginationLimitFlag = cli.StringFlag{
|
||||||
|
Name: "limit",
|
||||||
|
Aliases: []string{"lm"},
|
||||||
|
Usage: "specify limit of items per page",
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginOutputFlags defines login and output flags that should
|
||||||
|
// added to all subcommands and appended to the flags of the
|
||||||
|
// subcommand to work around issue and provide --login and --output:
|
||||||
|
// https://github.com/urfave/cli/issues/585
|
||||||
|
var LoginOutputFlags = []cli.Flag{
|
||||||
|
&LoginFlag,
|
||||||
|
&OutputFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginRepoFlags defines login and repo flags that should
|
||||||
|
// be used for all subcommands and appended to the flags of
|
||||||
|
// the subcommand to work around issue and provide --login and --repo:
|
||||||
|
// https://github.com/urfave/cli/issues/585
|
||||||
|
var LoginRepoFlags = []cli.Flag{
|
||||||
|
&LoginFlag,
|
||||||
|
&RepoFlag,
|
||||||
|
&RemoteFlag,
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllDefaultFlags defines flags that should be available
|
||||||
|
// for all subcommands working with dedicated repositories
|
||||||
|
// to work around issue and provide --login, --repo and --output:
|
||||||
|
// https://github.com/urfave/cli/issues/585
|
||||||
|
var AllDefaultFlags = append([]cli.Flag{
|
||||||
|
&RepoFlag,
|
||||||
|
&RemoteFlag,
|
||||||
|
}, LoginOutputFlags...)
|
||||||
|
|
||||||
|
// NotificationFlags defines flags that should be available on notifications.
|
||||||
|
var NotificationFlags = append([]cli.Flag{
|
||||||
|
NotificationStateFlag,
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "mine",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Show notifications across all your repositories instead of the current repository only",
|
||||||
|
},
|
||||||
|
&PaginationPageFlag,
|
||||||
|
&PaginationLimitFlag,
|
||||||
|
}, AllDefaultFlags...)
|
||||||
|
|
||||||
|
// NotificationStateFlag is a csv flag applied to all notification subcommands as filter
|
||||||
|
var NotificationStateFlag = NewCsvFlag(
|
||||||
|
"states",
|
||||||
|
"notification states to filter by",
|
||||||
|
[]string{"s"},
|
||||||
|
[]string{"pinned", "unread", "read"},
|
||||||
|
[]string{"unread", "pinned"},
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldsFlag generates a flag selecting printable fields.
|
||||||
|
// To retrieve the value, use f.GetValues()
|
||||||
|
func FieldsFlag(availableFields, defaultFields []string) *CsvFlag {
|
||||||
|
return NewCsvFlag("fields", "fields to print", []string{"f"}, availableFields, defaultFields)
|
||||||
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"github.com/araddon/dateparse"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StateFlag provides flag to specify issue/pr state, defaulting to "open"
|
||||||
|
var StateFlag = cli.StringFlag{
|
||||||
|
Name: "state",
|
||||||
|
Usage: "Filter by state (all|open|closed)",
|
||||||
|
DefaultText: "open",
|
||||||
|
}
|
||||||
|
|
||||||
|
// MilestoneFilterFlag is a CSV flag used to filter issues by milestones
|
||||||
|
var MilestoneFilterFlag = NewCsvFlag(
|
||||||
|
"milestones",
|
||||||
|
"milestones to match issues against",
|
||||||
|
[]string{"m"}, nil, nil)
|
||||||
|
|
||||||
|
// LabelFilterFlag is a CSV flag used to filter issues by labels
|
||||||
|
var LabelFilterFlag = NewCsvFlag(
|
||||||
|
"labels",
|
||||||
|
"labels to match issues against",
|
||||||
|
[]string{"L"}, nil, nil)
|
||||||
|
|
||||||
|
// PRListingFlags defines flags that should be available on pr listing flags.
|
||||||
|
var PRListingFlags = append([]cli.Flag{
|
||||||
|
&StateFlag,
|
||||||
|
&PaginationPageFlag,
|
||||||
|
&PaginationLimitFlag,
|
||||||
|
}, AllDefaultFlags...)
|
||||||
|
|
||||||
|
// IssueListingFlags defines flags that should be available on issue listing flags.
|
||||||
|
var IssueListingFlags = append([]cli.Flag{
|
||||||
|
&StateFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "kind",
|
||||||
|
Aliases: []string{"K"},
|
||||||
|
Usage: "Whether to return `issues`, `pulls`, or `all` (you can use this to apply advanced search filters to PRs)",
|
||||||
|
DefaultText: "issues",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "keyword",
|
||||||
|
Aliases: []string{"k"},
|
||||||
|
Usage: "Filter by search string",
|
||||||
|
},
|
||||||
|
LabelFilterFlag,
|
||||||
|
MilestoneFilterFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "author",
|
||||||
|
Aliases: []string{"A"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "assignee",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "mentions",
|
||||||
|
Aliases: []string{"M"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner",
|
||||||
|
Aliases: []string{"org"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "from",
|
||||||
|
Aliases: []string{"F"},
|
||||||
|
Usage: "Filter by activity after this date",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "until",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Usage: "Filter by activity before this date",
|
||||||
|
},
|
||||||
|
&PaginationPageFlag,
|
||||||
|
&PaginationLimitFlag,
|
||||||
|
}, AllDefaultFlags...)
|
||||||
|
|
||||||
|
// issuePRFlags defines shared flags between flags IssuePRCreateFlags and IssuePREditFlags
|
||||||
|
var issuePRFlags = append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "title",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "referenced-version",
|
||||||
|
Aliases: []string{"v"},
|
||||||
|
Usage: "commit-hash or tag name to assign",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "milestone",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Milestone to assign",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "deadline",
|
||||||
|
Aliases: []string{"D"},
|
||||||
|
Usage: "Deadline timestamp to assign",
|
||||||
|
},
|
||||||
|
}, LoginRepoFlags...)
|
||||||
|
|
||||||
|
// IssuePRCreateFlags defines flags for creation of issues and PRs
|
||||||
|
var IssuePRCreateFlags = append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "assignees",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "Comma-separated list of usernames to assign",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "labels",
|
||||||
|
Aliases: []string{"L"},
|
||||||
|
Usage: "Comma-separated list of labels to assign",
|
||||||
|
},
|
||||||
|
}, issuePRFlags...)
|
||||||
|
|
||||||
|
// GetIssuePRCreateFlags parses all IssuePREditFlags
|
||||||
|
func GetIssuePRCreateFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) {
|
||||||
|
opts := gitea.CreateIssueOption{
|
||||||
|
Title: ctx.String("title"),
|
||||||
|
Body: ctx.String("description"),
|
||||||
|
Assignees: strings.Split(ctx.String("assignees"), ","),
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
date := ctx.String("deadline")
|
||||||
|
if date != "" {
|
||||||
|
t, err := dateparse.ParseAny(date)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.Deadline = &t
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
labelNames := strings.Split(ctx.String("labels"), ",")
|
||||||
|
if len(labelNames) != 0 {
|
||||||
|
if client == nil {
|
||||||
|
client = ctx.Login.Client()
|
||||||
|
}
|
||||||
|
if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 {
|
||||||
|
if client == nil {
|
||||||
|
client = ctx.Login.Client()
|
||||||
|
}
|
||||||
|
ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Milestone '%s' not found", milestoneName)
|
||||||
|
}
|
||||||
|
opts.Milestone = ms.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return &opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssuePREditFlags defines flags for editing properties of issues and PRs
|
||||||
|
var IssuePREditFlags = append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "add-assignees",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "Comma-separated list of usernames to assign",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "add-labels",
|
||||||
|
Aliases: []string{"L"},
|
||||||
|
Usage: "Comma-separated list of labels to assign. Takes precedence over --remove-labels",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "remove-labels",
|
||||||
|
Usage: "Comma-separated list of labels to remove",
|
||||||
|
},
|
||||||
|
}, issuePRFlags...)
|
||||||
|
|
||||||
|
// GetIssuePREditFlags parses all IssuePREditFlags
|
||||||
|
func GetIssuePREditFlags(ctx *context.TeaContext) (*task.EditIssueOption, error) {
|
||||||
|
opts := task.EditIssueOption{}
|
||||||
|
if ctx.IsSet("title") {
|
||||||
|
val := ctx.String("title")
|
||||||
|
opts.Title = &val
|
||||||
|
}
|
||||||
|
if ctx.IsSet("description") {
|
||||||
|
val := ctx.String("description")
|
||||||
|
opts.Body = &val
|
||||||
|
}
|
||||||
|
if ctx.IsSet("referenced-version") {
|
||||||
|
val := ctx.String("referenced-version")
|
||||||
|
opts.Ref = &val
|
||||||
|
}
|
||||||
|
if ctx.IsSet("milestone") {
|
||||||
|
val := ctx.String("milestone")
|
||||||
|
opts.Milestone = &val
|
||||||
|
}
|
||||||
|
if ctx.IsSet("deadline") {
|
||||||
|
date := ctx.String("deadline")
|
||||||
|
if date == "" {
|
||||||
|
opts.Deadline = &time.Time{}
|
||||||
|
} else {
|
||||||
|
t, err := dateparse.ParseAny(date)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.Deadline = &t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ctx.IsSet("add-assignees") {
|
||||||
|
val := ctx.String("add-assignees")
|
||||||
|
opts.AddAssignees = strings.Split(val, ",")
|
||||||
|
}
|
||||||
|
if ctx.IsSet("add-labels") {
|
||||||
|
val := ctx.String("add-labels")
|
||||||
|
opts.AddLabels = strings.Split(val, ",")
|
||||||
|
}
|
||||||
|
if ctx.IsSet("remove-labels") {
|
||||||
|
val := ctx.String("remove-labels")
|
||||||
|
opts.RemoveLabels = strings.Split(val, ",")
|
||||||
|
}
|
||||||
|
return &opts, nil
|
||||||
|
}
|
162
cmd/issues.go
162
cmd/issues.go
|
@ -1,157 +1,75 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/tea/cmd/issues"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdIssues represents to login a gitea server.
|
// CmdIssues represents to login a gitea server.
|
||||||
var CmdIssues = cli.Command{
|
var CmdIssues = cli.Command{
|
||||||
Name: "issues",
|
Name: "issues",
|
||||||
Usage: "List and create issues",
|
Aliases: []string{"issue", "i"},
|
||||||
Description: `List and create issues`,
|
Category: catEntities,
|
||||||
|
Usage: "List, create and update issues",
|
||||||
|
Description: `Lists issues when called without argument. If issue index is provided, will show it in detail.`,
|
||||||
|
ArgsUsage: "[<issue index>]",
|
||||||
Action: runIssues,
|
Action: runIssues,
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
CmdIssuesList,
|
&issues.CmdIssuesList,
|
||||||
CmdIssuesCreate,
|
&issues.CmdIssuesCreate,
|
||||||
|
&issues.CmdIssuesEdit,
|
||||||
|
&issues.CmdIssuesReopen,
|
||||||
|
&issues.CmdIssuesClose,
|
||||||
},
|
},
|
||||||
Flags: AllDefaultFlags,
|
Flags: append([]cli.Flag{
|
||||||
}
|
&cli.BoolFlag{
|
||||||
|
Name: "comments",
|
||||||
// CmdIssuesList represents a sub command of issues to list issues
|
Usage: "Whether to display comments (will prompt if not provided & run interactively)",
|
||||||
var CmdIssuesList = cli.Command{
|
},
|
||||||
Name: "ls",
|
}, issues.CmdIssuesList.Flags...),
|
||||||
Usage: "List issues of the repository",
|
|
||||||
Description: `List issues of the repository`,
|
|
||||||
Action: runIssuesList,
|
|
||||||
Flags: AllDefaultFlags,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runIssues(ctx *cli.Context) error {
|
func runIssues(ctx *cli.Context) error {
|
||||||
if len(os.Args) == 3 {
|
if ctx.Args().Len() == 1 {
|
||||||
return runIssueDetail(ctx, os.Args[2])
|
return runIssueDetail(ctx, ctx.Args().First())
|
||||||
}
|
}
|
||||||
return runIssuesList(ctx)
|
return issues.RunIssuesList(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runIssueDetail(ctx *cli.Context, index string) error {
|
func runIssueDetail(cmd *cli.Context, index string) error {
|
||||||
login, owner, repo := initCommand()
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
if strings.HasPrefix(index, "#") {
|
idx, err := utils.ArgToIndex(index)
|
||||||
index = index[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
idx, err := strconv.ParseInt(index, 10, 64)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
client := ctx.Login.Client()
|
||||||
issue, err := login.Client().GetIssue(owner, 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)
|
||||||
fmt.Printf("#%d %s\n%s created %s\n\n%s", issue.Index,
|
|
||||||
issue.Title,
|
|
||||||
issue.Poster.UserName,
|
|
||||||
issue.Created.Format("2006-01-02 15:04:05"),
|
|
||||||
issue.Body,
|
|
||||||
)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runIssuesList(ctx *cli.Context) error {
|
|
||||||
login, owner, repo := initCommand()
|
|
||||||
|
|
||||||
issues, err := login.Client().ListRepoIssues(owner, repo, gitea.ListIssueOption{
|
|
||||||
Page: 0,
|
|
||||||
State: string(gitea.StateOpen),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return err
|
||||||
}
|
}
|
||||||
|
print.IssueDetails(issue, reactions)
|
||||||
|
|
||||||
headers := []string{
|
if issue.Comments > 0 {
|
||||||
"Index",
|
err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments)
|
||||||
"Name",
|
|
||||||
"Updated",
|
|
||||||
"Title",
|
|
||||||
}
|
|
||||||
|
|
||||||
var values [][]string
|
|
||||||
|
|
||||||
if len(issues) == 0 {
|
|
||||||
Output(outputValue, headers, values)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, issue := range issues {
|
|
||||||
name := issue.Poster.FullName
|
|
||||||
if len(name) == 0 {
|
|
||||||
name = issue.Poster.UserName
|
|
||||||
}
|
|
||||||
values = append(
|
|
||||||
values,
|
|
||||||
[]string{
|
|
||||||
strconv.FormatInt(issue.Index, 10),
|
|
||||||
name,
|
|
||||||
issue.Updated.Format("2006-01-02 15:04:05"),
|
|
||||||
issue.Title,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Output(outputValue, headers, values)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdIssuesCreate represents a sub command of issues to create issue
|
|
||||||
var CmdIssuesCreate = cli.Command{
|
|
||||||
Name: "create",
|
|
||||||
Usage: "Create an issue on repository",
|
|
||||||
Description: `Create an issue on repository`,
|
|
||||||
Action: runIssuesCreate,
|
|
||||||
Flags: append([]cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "title, t",
|
|
||||||
Usage: "issue title to create",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "body, b",
|
|
||||||
Usage: "issue body to create",
|
|
||||||
},
|
|
||||||
}, LoginRepoFlags...),
|
|
||||||
}
|
|
||||||
|
|
||||||
func runIssuesCreate(ctx *cli.Context) error {
|
|
||||||
login, owner, repo := initCommand()
|
|
||||||
|
|
||||||
_, err := login.Client().CreateIssue(owner, repo, gitea.CreateIssueOption{
|
|
||||||
Title: ctx.String("title"),
|
|
||||||
Body: ctx.String("body"),
|
|
||||||
// TODO:
|
|
||||||
//Assignee string `json:"assignee"`
|
|
||||||
//Assignees []string `json:"assignees"`
|
|
||||||
//Deadline *time.Time `json:"due_date"`
|
|
||||||
//Milestone int64 `json:"milestone"`
|
|
||||||
//Labels []int64 `json:"labels"`
|
|
||||||
//Closed bool `json:"closed"`
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return fmt.Errorf("error loading comments: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package issues
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdIssuesClose represents a sub command of issues to close an issue
|
||||||
|
var CmdIssuesClose = cli.Command{
|
||||||
|
Name: "close",
|
||||||
|
Usage: "Change state of one ore more issues to 'closed'",
|
||||||
|
Description: `Change state of one ore more issues to 'closed'`,
|
||||||
|
ArgsUsage: "<issue index> [<issue index>...]",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
var s = gitea.StateClosed
|
||||||
|
return editIssueState(ctx, gitea.EditIssueOption{State: &s})
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
// editIssueState abstracts the arg parsing to edit the given issue
|
||||||
|
func editIssueState(cmd *cli.Context, opts gitea.EditIssueOption) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
if ctx.Args().Len() == 0 {
|
||||||
|
return fmt.Errorf(ctx.Command.ArgsUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
indices, err := utils.ArgsToIndices(ctx.Args().Slice())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
for _, index := range indices {
|
||||||
|
issue, _, err := client.EditIssue(ctx.Owner, ctx.Repo, index, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(indices) > 1 {
|
||||||
|
fmt.Println(issue.HTMLURL)
|
||||||
|
} else {
|
||||||
|
print.IssueDetails(issue, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package issues
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdIssuesCreate represents a sub command of issues to create issue
|
||||||
|
var CmdIssuesCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create an issue on repository",
|
||||||
|
Description: `Create an issue on repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runIssuesCreate,
|
||||||
|
Flags: flags.IssuePRCreateFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runIssuesCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
if ctx.NumFlags() == 0 {
|
||||||
|
return interact.CreateIssue(ctx.Login, ctx.Owner, ctx.Repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts, err := flags.GetIssuePRCreateFlags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.CreateIssue(
|
||||||
|
ctx.Login,
|
||||||
|
ctx.Owner,
|
||||||
|
ctx.Repo,
|
||||||
|
*opts,
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package issues
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdIssuesEdit is the subcommand of issues to edit issues
|
||||||
|
var CmdIssuesEdit = cli.Command{
|
||||||
|
Name: "edit",
|
||||||
|
Aliases: []string{"e"},
|
||||||
|
Usage: "Edit one or more issues",
|
||||||
|
Description: `Edit one or more issues. To unset a property again,
|
||||||
|
use an empty string (eg. --milestone "").`,
|
||||||
|
ArgsUsage: "<idx> [<idx>...]",
|
||||||
|
Action: runIssuesEdit,
|
||||||
|
Flags: flags.IssuePREditFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runIssuesEdit(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
if !cmd.Args().Present() {
|
||||||
|
return fmt.Errorf("must specify at least one issue index")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts, err := flags.GetIssuePREditFlags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
indices, err := utils.ArgsToIndices(ctx.Args().Slice())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
for _, opts.Index = range indices {
|
||||||
|
issue, err := task.EditIssue(ctx, client, *opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ctx.Args().Len() > 1 {
|
||||||
|
fmt.Println(issue.HTMLURL)
|
||||||
|
} else {
|
||||||
|
print.IssueDetails(issue, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package issues
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/araddon/dateparse"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var issueFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{
|
||||||
|
"index", "title", "state", "author", "milestone", "labels", "owner", "repo",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdIssuesList represents a sub command of issues to list issues
|
||||||
|
var CmdIssuesList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List issues of the repository",
|
||||||
|
Description: `List issues of the repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunIssuesList,
|
||||||
|
Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssueListingFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunIssuesList list issues
|
||||||
|
func RunIssuesList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
state := gitea.StateOpen
|
||||||
|
switch ctx.String("state") {
|
||||||
|
case "all":
|
||||||
|
state = gitea.StateAll
|
||||||
|
case "", "open":
|
||||||
|
state = gitea.StateOpen
|
||||||
|
case "closed":
|
||||||
|
state = gitea.StateClosed
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown state '%s'", ctx.String("state"))
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := gitea.IssueTypeIssue
|
||||||
|
switch ctx.String("kind") {
|
||||||
|
case "", "issues", "issue":
|
||||||
|
kind = gitea.IssueTypeIssue
|
||||||
|
case "pulls", "pull", "pr":
|
||||||
|
kind = gitea.IssueTypePull
|
||||||
|
case "all":
|
||||||
|
kind = gitea.IssueTypeAll
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown kind '%s'", ctx.String("kind"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var from, until time.Time
|
||||||
|
if ctx.IsSet("from") {
|
||||||
|
from, err = dateparse.ParseLocal(ctx.String("from"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ctx.IsSet("until") {
|
||||||
|
until, err = dateparse.ParseLocal(ctx.String("until"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
owner := ctx.Owner
|
||||||
|
if ctx.IsSet("owner") {
|
||||||
|
owner = ctx.String("owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore error, as we don't do any input validation on these flags
|
||||||
|
labels, _ := flags.LabelFilterFlag.GetValues(cmd)
|
||||||
|
milestones, _ := flags.MilestoneFilterFlag.GetValues(cmd)
|
||||||
|
var issues []*gitea.Issue
|
||||||
|
if ctx.Repo != "" {
|
||||||
|
issues, _, err = ctx.Login.Client().ListRepoIssues(owner, ctx.Repo, gitea.ListIssueOption{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
State: state,
|
||||||
|
Type: kind,
|
||||||
|
KeyWord: ctx.String("keyword"),
|
||||||
|
CreatedBy: ctx.String("author"),
|
||||||
|
AssignedBy: ctx.String("assigned-to"),
|
||||||
|
MentionedBy: ctx.String("mentions"),
|
||||||
|
Labels: labels,
|
||||||
|
Milestones: milestones,
|
||||||
|
Since: from,
|
||||||
|
Before: until,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
issues, _, err = ctx.Login.Client().ListIssues(gitea.ListIssueOption{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
State: state,
|
||||||
|
Type: kind,
|
||||||
|
KeyWord: ctx.String("keyword"),
|
||||||
|
CreatedBy: ctx.String("author"),
|
||||||
|
AssignedBy: ctx.String("assigned-to"),
|
||||||
|
MentionedBy: ctx.String("mentions"),
|
||||||
|
Labels: labels,
|
||||||
|
Milestones: milestones,
|
||||||
|
Since: from,
|
||||||
|
Before: until,
|
||||||
|
Owner: owner,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := issueFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.IssuesPullsList(issues, ctx.Output, fields)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package issues
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdIssuesReopen represents a sub command of issues to open an issue
|
||||||
|
var CmdIssuesReopen = cli.Command{
|
||||||
|
Name: "reopen",
|
||||||
|
Aliases: []string{"open"},
|
||||||
|
Usage: "Change state of one or more issues to 'open'",
|
||||||
|
Description: `Change state of one or more issues to 'open'`,
|
||||||
|
ArgsUsage: "<issue index> [<issue index>...]",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
var s = gitea.StateOpen
|
||||||
|
return editIssueState(ctx, gitea.EditIssueOption{State: &s})
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
261
cmd/labels.go
261
cmd/labels.go
|
@ -1,265 +1,40 @@
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/tea/cmd/labels"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdLabels represents to operate repositories' labels.
|
// CmdLabels represents to operate repositories' labels.
|
||||||
var CmdLabels = cli.Command{
|
var CmdLabels = cli.Command{
|
||||||
Name: "labels",
|
Name: "labels",
|
||||||
|
Aliases: []string{"label"},
|
||||||
|
Category: catEntities,
|
||||||
Usage: "Manage issue labels",
|
Usage: "Manage issue labels",
|
||||||
Description: `Manage issue labels`,
|
Description: `Manage issue labels`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
Action: runLabels,
|
Action: runLabels,
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
CmdLabelCreate,
|
&labels.CmdLabelsList,
|
||||||
CmdLabelUpdate,
|
&labels.CmdLabelCreate,
|
||||||
CmdLabelDelete,
|
&labels.CmdLabelUpdate,
|
||||||
|
&labels.CmdLabelDelete,
|
||||||
},
|
},
|
||||||
Flags: append([]cli.Flag{
|
Flags: labels.CmdLabelsList.Flags,
|
||||||
cli.StringFlag{
|
|
||||||
Name: "save, s",
|
|
||||||
Usage: "Save all the labels as a file",
|
|
||||||
},
|
|
||||||
}, AllDefaultFlags...),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLabels(ctx *cli.Context) error {
|
func runLabels(ctx *cli.Context) error {
|
||||||
login, owner, repo := initCommand()
|
if ctx.Args().Len() == 1 {
|
||||||
|
return runLabelsDetails(ctx)
|
||||||
headers := []string{
|
}
|
||||||
"Index",
|
return labels.RunLabelsList(ctx)
|
||||||
"Color",
|
|
||||||
"Name",
|
|
||||||
"Description",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var values [][]string
|
func runLabelsDetails(ctx *cli.Context) error {
|
||||||
|
return fmt.Errorf("Not yet implemented")
|
||||||
labels, err := login.Client().ListRepoLabels(owner, repo)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(labels) == 0 {
|
|
||||||
Output(outputValue, headers, values)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fPath := ctx.String("save")
|
|
||||||
if len(fPath) > 0 {
|
|
||||||
f, err := os.Create(fPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
for _, label := range labels {
|
|
||||||
fmt.Fprintf(f, "#%s %s\n", label.Color, label.Name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, label := range labels {
|
|
||||||
values = append(
|
|
||||||
values,
|
|
||||||
[]string{
|
|
||||||
strconv.FormatInt(label.ID, 10),
|
|
||||||
label.Color,
|
|
||||||
label.Name,
|
|
||||||
label.Description,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Output(outputValue, headers, values)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdLabelCreate represents a sub command of labels to create label.
|
|
||||||
var CmdLabelCreate = cli.Command{
|
|
||||||
Name: "create",
|
|
||||||
Usage: "Create a label",
|
|
||||||
Description: `Create a label`,
|
|
||||||
Action: runLabelCreate,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Usage: "label name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "color",
|
|
||||||
Usage: "label color value",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "description",
|
|
||||||
Usage: "label description",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "file",
|
|
||||||
Usage: "indicate a label file",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitLabelLine(line string) (string, string, string) {
|
|
||||||
fields := strings.SplitN(line, ";", 2)
|
|
||||||
var color, name, description string
|
|
||||||
if len(fields) < 1 {
|
|
||||||
return "", "", ""
|
|
||||||
} else if len(fields) >= 2 {
|
|
||||||
description = strings.TrimSpace(fields[1])
|
|
||||||
}
|
|
||||||
fields = strings.Fields(fields[0])
|
|
||||||
if len(fields) <= 0 {
|
|
||||||
return "", "", ""
|
|
||||||
}
|
|
||||||
color = fields[0]
|
|
||||||
if len(fields) == 2 {
|
|
||||||
name = fields[1]
|
|
||||||
} else if len(fields) > 2 {
|
|
||||||
name = strings.Join(fields[1:], " ")
|
|
||||||
}
|
|
||||||
return color, name, description
|
|
||||||
}
|
|
||||||
|
|
||||||
func runLabelCreate(ctx *cli.Context) error {
|
|
||||||
login, owner, repo := initCommand()
|
|
||||||
|
|
||||||
labelFile := ctx.String("file")
|
|
||||||
var err error
|
|
||||||
if len(labelFile) == 0 {
|
|
||||||
_, err = login.Client().CreateLabel(owner, repo, gitea.CreateLabelOption{
|
|
||||||
Name: ctx.String("name"),
|
|
||||||
Color: ctx.String("color"),
|
|
||||||
Description: ctx.String("description"),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
f, err := os.Open(labelFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
var i = 1
|
|
||||||
// FIXME: if Gitea's API support create multiple labels once, we should move to that API.
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
color, name, description := splitLabelLine(line)
|
|
||||||
if color == "" || name == "" {
|
|
||||||
log.Printf("Line %d ignored because lack of enough fields: %s\n", i, line)
|
|
||||||
} else {
|
|
||||||
_, err = login.Client().CreateLabel(owner, repo, gitea.CreateLabelOption{
|
|
||||||
Name: name,
|
|
||||||
Color: color,
|
|
||||||
Description: description,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdLabelUpdate represents a sub command of labels to update label.
|
|
||||||
var CmdLabelUpdate = cli.Command{
|
|
||||||
Name: "update",
|
|
||||||
Usage: "Update a label",
|
|
||||||
Description: `Update a label`,
|
|
||||||
Action: runLabelUpdate,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "id",
|
|
||||||
Usage: "label id",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Usage: "label name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "color",
|
|
||||||
Usage: "label color value",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "description",
|
|
||||||
Usage: "label description",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runLabelUpdate(ctx *cli.Context) error {
|
|
||||||
login, owner, repo := initCommand()
|
|
||||||
|
|
||||||
id := ctx.Int64("id")
|
|
||||||
var pName, pColor, pDescription *string
|
|
||||||
name := ctx.String("name")
|
|
||||||
if name != "" {
|
|
||||||
pName = &name
|
|
||||||
}
|
|
||||||
|
|
||||||
color := ctx.String("color")
|
|
||||||
if color != "" {
|
|
||||||
pColor = &color
|
|
||||||
}
|
|
||||||
|
|
||||||
description := ctx.String("description")
|
|
||||||
if description != "" {
|
|
||||||
pDescription = &description
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
_, err = login.Client().EditLabel(owner, repo, id, gitea.EditLabelOption{
|
|
||||||
Name: pName,
|
|
||||||
Color: pColor,
|
|
||||||
Description: pDescription,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdLabelDelete represents a sub command of labels to delete label.
|
|
||||||
var CmdLabelDelete = cli.Command{
|
|
||||||
Name: "delete",
|
|
||||||
Usage: "Delete a label",
|
|
||||||
Description: `Delete a label`,
|
|
||||||
Action: runLabelCreate,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "id",
|
|
||||||
Usage: "label id",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runLabelDelete(ctx *cli.Context) error {
|
|
||||||
login, owner, repo := initCommand()
|
|
||||||
|
|
||||||
err := login.Client().DeleteLabel(owner, repo, ctx.Int64("id"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package labels
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLabelCreate represents a sub command of labels to create label.
|
||||||
|
var CmdLabelCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create a label",
|
||||||
|
Description: `Create a label`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runLabelCreate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Usage: "label name",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "color",
|
||||||
|
Usage: "label color value",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Usage: "label description",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "file",
|
||||||
|
Usage: "indicate a label file",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLabelCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
labelFile := ctx.String("file")
|
||||||
|
var err error
|
||||||
|
if len(labelFile) == 0 {
|
||||||
|
_, _, err = ctx.Login.Client().CreateLabel(ctx.Owner, ctx.Repo, gitea.CreateLabelOption{
|
||||||
|
Name: ctx.String("name"),
|
||||||
|
Color: ctx.String("color"),
|
||||||
|
Description: ctx.String("description"),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
f, err := os.Open(labelFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
var i = 1
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
color, name, description := splitLabelLine(line)
|
||||||
|
if color == "" || name == "" {
|
||||||
|
log.Printf("Line %d ignored because lack of enough fields: %s\n", i, line)
|
||||||
|
} else {
|
||||||
|
_, _, err = ctx.Login.Client().CreateLabel(ctx.Owner, ctx.Repo, gitea.CreateLabelOption{
|
||||||
|
Name: name,
|
||||||
|
Color: color,
|
||||||
|
Description: description,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitLabelLine(line string) (string, string, string) {
|
||||||
|
fields := strings.SplitN(line, ";", 2)
|
||||||
|
var color, name, description string
|
||||||
|
if len(fields) < 1 {
|
||||||
|
return "", "", ""
|
||||||
|
} else if len(fields) >= 2 {
|
||||||
|
description = strings.TrimSpace(fields[1])
|
||||||
|
}
|
||||||
|
fields = strings.Fields(fields[0])
|
||||||
|
if len(fields) <= 0 {
|
||||||
|
return "", "", ""
|
||||||
|
}
|
||||||
|
color = fields[0]
|
||||||
|
if len(fields) == 2 {
|
||||||
|
name = fields[1]
|
||||||
|
} else if len(fields) > 2 {
|
||||||
|
name = strings.Join(fields[1:], " ")
|
||||||
|
}
|
||||||
|
return color, name, description
|
||||||
|
}
|
|
@ -1,8 +1,7 @@
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
package labels
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package labels
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLabelDelete represents a sub command of labels to delete label.
|
||||||
|
var CmdLabelDelete = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "Delete a label",
|
||||||
|
Description: `Delete a label`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runLabelDelete,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "id",
|
||||||
|
Usage: "label id",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLabelDelete(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
_, err := ctx.Login.Client().DeleteLabel(ctx.Owner, ctx.Repo, ctx.Int64("id"))
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package labels
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLabelsList represents a sub command of labels to list labels
|
||||||
|
var CmdLabelsList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List labels",
|
||||||
|
Description: "List labels",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunLabelsList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "save",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Usage: "Save all the labels as a file",
|
||||||
|
},
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunLabelsList list labels.
|
||||||
|
func RunLabelsList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
labels, _, err := client.ListRepoLabels(ctx.Owner, ctx.Repo, gitea.ListLabelsOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet("save") {
|
||||||
|
return task.LabelsExport(labels, ctx.String("save"))
|
||||||
|
}
|
||||||
|
|
||||||
|
print.LabelsList(labels, ctx.Output)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package labels
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLabelUpdate represents a sub command of labels to update label.
|
||||||
|
var CmdLabelUpdate = cli.Command{
|
||||||
|
Name: "update",
|
||||||
|
Usage: "Update a label",
|
||||||
|
Description: `Update a label`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runLabelUpdate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.IntFlag{
|
||||||
|
Name: "id",
|
||||||
|
Usage: "label id",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Usage: "label name",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "color",
|
||||||
|
Usage: "label color value",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Usage: "label description",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLabelUpdate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
id := ctx.Int64("id")
|
||||||
|
var pName, pColor, pDescription *string
|
||||||
|
name := ctx.String("name")
|
||||||
|
if name != "" {
|
||||||
|
pName = &name
|
||||||
|
}
|
||||||
|
|
||||||
|
color := ctx.String("color")
|
||||||
|
if color != "" {
|
||||||
|
pColor = &color
|
||||||
|
}
|
||||||
|
|
||||||
|
description := ctx.String("description")
|
||||||
|
if description != "" {
|
||||||
|
pDescription = &description
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
_, _, err = ctx.Login.Client().EditLabel(ctx.Owner, ctx.Repo, id, gitea.EditLabelOption{
|
||||||
|
Name: pName,
|
||||||
|
Color: pColor,
|
||||||
|
Description: pDescription,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
111
cmd/log.go
111
cmd/log.go
|
@ -1,111 +0,0 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
showLog bool
|
|
||||||
)
|
|
||||||
|
|
||||||
// Println println content according the flag
|
|
||||||
func Println(a ...interface{}) {
|
|
||||||
if showLog {
|
|
||||||
fmt.Println(a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf printf content according the flag
|
|
||||||
func Printf(format string, a ...interface{}) {
|
|
||||||
if showLog {
|
|
||||||
fmt.Printf(format, a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error println content as an error information
|
|
||||||
func Error(a ...interface{}) {
|
|
||||||
fmt.Println(a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf printf content as an error information
|
|
||||||
func Errorf(format string, a ...interface{}) {
|
|
||||||
fmt.Printf(format, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// outputtable prints structured data as table
|
|
||||||
func outputtable(headers []string, values [][]string) {
|
|
||||||
table := tablewriter.NewWriter(os.Stdout)
|
|
||||||
if len(headers) > 0 {
|
|
||||||
table.SetHeader(headers)
|
|
||||||
}
|
|
||||||
for _, value := range values {
|
|
||||||
table.Append(value)
|
|
||||||
}
|
|
||||||
table.Render()
|
|
||||||
}
|
|
||||||
|
|
||||||
// outputsimple prints structured data as space delimited value
|
|
||||||
func outputsimple(headers []string, values [][]string) {
|
|
||||||
for _, value := range values {
|
|
||||||
fmt.Printf(strings.Join(value, " "))
|
|
||||||
fmt.Printf("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// outputdsv prints structured data as delimiter separated value format
|
|
||||||
func outputdsv(headers []string, values [][]string, delimiterOpt ...string) {
|
|
||||||
delimiter := ","
|
|
||||||
if len(delimiterOpt) > 0 {
|
|
||||||
delimiter = delimiterOpt[0]
|
|
||||||
}
|
|
||||||
fmt.Println("\"" + strings.Join(headers, "\""+delimiter+"\"") + "\"")
|
|
||||||
for _, value := range values {
|
|
||||||
fmt.Printf("\"")
|
|
||||||
fmt.Printf(strings.Join(value, "\""+delimiter+"\""))
|
|
||||||
fmt.Printf("\"")
|
|
||||||
fmt.Printf("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// outputyaml prints structured data as yaml
|
|
||||||
func outputyaml(headers []string, values [][]string) {
|
|
||||||
for _, value := range values {
|
|
||||||
fmt.Println("-")
|
|
||||||
for j, val := range value {
|
|
||||||
intVal, _ := strconv.Atoi(val)
|
|
||||||
if strconv.Itoa(intVal) == val {
|
|
||||||
fmt.Printf(" %s: %s\n", headers[j], val)
|
|
||||||
} else {
|
|
||||||
fmt.Printf(" %s: '%s'\n", headers[j], val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output provides general function to convert given information
|
|
||||||
// into several outputs
|
|
||||||
func Output(output string, headers []string, values [][]string) {
|
|
||||||
switch {
|
|
||||||
case output == "" || output == "table":
|
|
||||||
outputtable(headers, values)
|
|
||||||
case output == "csv":
|
|
||||||
outputdsv(headers, values, ",")
|
|
||||||
case output == "simple":
|
|
||||||
outputsimple(headers, values)
|
|
||||||
case output == "tsv":
|
|
||||||
outputdsv(headers, values, "\t")
|
|
||||||
case output == "yaml":
|
|
||||||
outputyaml(headers, values)
|
|
||||||
default:
|
|
||||||
Errorf("unknown output type '" + output + "', available types are:\n- csv: comma-separated values\n- simple: space-separated values\n- table: auto-aligned table format (default)\n- tsv: tab-separated values\n- yaml: YAML format\n")
|
|
||||||
}
|
|
||||||
}
|
|
137
cmd/login.go
137
cmd/login.go
|
@ -1,132 +1,51 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/http/cookiejar"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/tea/cmd/login"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdLogin represents to login a gitea server.
|
// CmdLogin represents to login a gitea server.
|
||||||
var CmdLogin = cli.Command{
|
var CmdLogin = cli.Command{
|
||||||
Name: "login",
|
Name: "logins",
|
||||||
|
Aliases: []string{"login"},
|
||||||
|
Category: catSetup,
|
||||||
Usage: "Log in to a Gitea server",
|
Usage: "Log in to a Gitea server",
|
||||||
Description: `Log in to a Gitea server`,
|
Description: `Log in to a Gitea server`,
|
||||||
Subcommands: []cli.Command{
|
ArgsUsage: "[<login name>]",
|
||||||
cmdLoginList,
|
Action: runLogins,
|
||||||
cmdLoginAdd,
|
Subcommands: []*cli.Command{
|
||||||
|
&login.CmdLoginList,
|
||||||
|
&login.CmdLoginAdd,
|
||||||
|
&login.CmdLoginEdit,
|
||||||
|
&login.CmdLoginDelete,
|
||||||
|
&login.CmdLoginSetDefault,
|
||||||
|
&login.CmdLoginHelper,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmdLogin represents to login a gitea server.
|
func runLogins(ctx *cli.Context) error {
|
||||||
var cmdLoginAdd = cli.Command{
|
if ctx.Args().Len() == 1 {
|
||||||
Name: "add",
|
return runLoginDetail(ctx.Args().First())
|
||||||
Usage: "Add a Gitea login",
|
}
|
||||||
Description: `Add a Gitea login`,
|
return login.RunLoginList(ctx)
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name, n",
|
|
||||||
Usage: "Login name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "url, u",
|
|
||||||
Value: "https://try.gitea.io",
|
|
||||||
EnvVar: "GITEA_SERVER_URL",
|
|
||||||
Usage: "Server URL",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "token, t",
|
|
||||||
Value: "",
|
|
||||||
EnvVar: "GITEA_SERVER_TOKEN",
|
|
||||||
Usage: "Access token. Can be obtained from Settings > Applications",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "insecure, i",
|
|
||||||
Usage: "Disable TLS verification",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: runLoginAdd,
|
|
||||||
}
|
|
||||||
|
|
||||||
func runLoginAdd(ctx *cli.Context) error {
|
|
||||||
if !ctx.IsSet("url") {
|
|
||||||
log.Fatal("You have to input Gitea server URL")
|
|
||||||
}
|
|
||||||
if !ctx.IsSet("token") {
|
|
||||||
log.Fatal("No token found")
|
|
||||||
}
|
|
||||||
if !ctx.IsSet("name") {
|
|
||||||
log.Fatal("You have to set a name for the login")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := loadConfig(yamlConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to load config file " + yamlConfigPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
client := gitea.NewClient(ctx.String("url"), ctx.String("token"))
|
|
||||||
if ctx.Bool("insecure") {
|
|
||||||
cookieJar, _ := cookiejar.New(nil)
|
|
||||||
|
|
||||||
client.SetHTTPClient(&http.Client{
|
|
||||||
Jar: cookieJar,
|
|
||||||
Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
u, err := client.GetMyUserInfo()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Login successful! Login name " + u.UserName)
|
|
||||||
|
|
||||||
err = addLogin(Login{
|
|
||||||
Name: ctx.String("name"),
|
|
||||||
URL: ctx.String("url"),
|
|
||||||
Token: ctx.String("token"),
|
|
||||||
Insecure: ctx.Bool("insecure"),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = saveConfig(yamlConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runLoginDetail(name string) error {
|
||||||
|
l := config.GetLoginByName(name)
|
||||||
|
if l == nil {
|
||||||
|
fmt.Printf("Login '%s' do not exist\n\n", name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmdLogin represents to login a gitea server.
|
print.LoginDetails(l)
|
||||||
var cmdLoginList = cli.Command{
|
|
||||||
Name: "ls",
|
|
||||||
Usage: "List Gitea logins",
|
|
||||||
Description: `List Gitea logins`,
|
|
||||||
Action: runLoginList,
|
|
||||||
}
|
|
||||||
|
|
||||||
func runLoginList(ctx *cli.Context) error {
|
|
||||||
err := loadConfig(yamlConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to load config file " + yamlConfigPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Name\tURL\tSSHHost\n")
|
|
||||||
for _, l := range config.Logins {
|
|
||||||
fmt.Printf("%s\t%s\t%s\n", l.Name, l.URL, l.GetSSHHost())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package login
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLoginAdd represents to login a gitea server.
|
||||||
|
var CmdLoginAdd = cli.Command{
|
||||||
|
Name: "add",
|
||||||
|
Usage: "Add a Gitea login",
|
||||||
|
Description: `Add a Gitea login, without args it will create one interactively`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Usage: "Login name",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "url",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Value: "https://gitea.com",
|
||||||
|
EnvVars: []string{"GITEA_SERVER_URL"},
|
||||||
|
Usage: "Server URL",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "no-version-check",
|
||||||
|
Aliases: []string{"nv"},
|
||||||
|
Usage: "Do not check version of Gitea instance",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "token",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Value: "",
|
||||||
|
EnvVars: []string{"GITEA_SERVER_TOKEN"},
|
||||||
|
Usage: "Access token. Can be obtained from Settings > Applications",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "user",
|
||||||
|
Value: "",
|
||||||
|
EnvVars: []string{"GITEA_SERVER_USER"},
|
||||||
|
Usage: "User for basic auth (will create token)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Aliases: []string{"pwd"},
|
||||||
|
Value: "",
|
||||||
|
EnvVars: []string{"GITEA_SERVER_PASSWORD"},
|
||||||
|
Usage: "Password for basic auth (will create token)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "otp",
|
||||||
|
EnvVars: []string{"GITEA_SERVER_OTP"},
|
||||||
|
Usage: "OTP token for auth, if necessary",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "scopes",
|
||||||
|
EnvVars: []string{"GITEA_SCOPES"},
|
||||||
|
Usage: "Token scopes to add when creating a new token, separated by a comma",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "ssh-key",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Usage: "Path to a SSH key/certificate to use, overrides auto-discovery",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "insecure",
|
||||||
|
Aliases: []string{"i"},
|
||||||
|
Usage: "Disable TLS verification",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "ssh-agent-principal",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Use SSH certificate with specified principal to login (needs a running ssh-agent with certificate loaded)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "ssh-agent-key",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "Use SSH public key or SSH fingerprint to login (needs a running ssh-agent with ssh key loaded)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "helper",
|
||||||
|
Aliases: []string{"j"},
|
||||||
|
Usage: "Add helper",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: runLoginAdd,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLoginAdd(ctx *cli.Context) error {
|
||||||
|
// if no args create login interactive
|
||||||
|
if ctx.NumFlags() == 0 {
|
||||||
|
return interact.CreateLogin()
|
||||||
|
}
|
||||||
|
|
||||||
|
sshAgent := false
|
||||||
|
if ctx.String("ssh-agent-key") != "" || ctx.String("ssh-agent-principal") != "" {
|
||||||
|
sshAgent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// else use args to add login
|
||||||
|
return task.CreateLogin(
|
||||||
|
ctx.String("name"),
|
||||||
|
ctx.String("token"),
|
||||||
|
ctx.String("user"),
|
||||||
|
ctx.String("password"),
|
||||||
|
ctx.String("otp"),
|
||||||
|
ctx.String("scopes"),
|
||||||
|
ctx.String("ssh-key"),
|
||||||
|
ctx.String("url"),
|
||||||
|
ctx.String("ssh-agent-principal"),
|
||||||
|
ctx.String("ssh-agent-key"),
|
||||||
|
ctx.Bool("insecure"),
|
||||||
|
sshAgent,
|
||||||
|
!ctx.Bool("no-version-check"),
|
||||||
|
ctx.Bool("helper"),
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package login
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLoginSetDefault represents to login a gitea server.
|
||||||
|
var CmdLoginSetDefault = cli.Command{
|
||||||
|
Name: "default",
|
||||||
|
Usage: "Get or Set Default Login",
|
||||||
|
Description: `Get or Set Default Login`,
|
||||||
|
ArgsUsage: "<Login>",
|
||||||
|
Action: runLoginSetDefault,
|
||||||
|
Flags: []cli.Flag{&flags.OutputFlag},
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLoginSetDefault(ctx *cli.Context) error {
|
||||||
|
if ctx.Args().Len() == 0 {
|
||||||
|
l, err := config.GetDefaultLogin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Default Login: %s\n", l.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
name := ctx.Args().First()
|
||||||
|
return config.SetDefaultLogin(name)
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package login
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLoginDelete is a command to delete a login
|
||||||
|
var CmdLoginDelete = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "Remove a Gitea login",
|
||||||
|
Description: `Remove a Gitea login`,
|
||||||
|
ArgsUsage: "<login name>",
|
||||||
|
Action: RunLoginDelete,
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunLoginDelete runs the action of a login delete command
|
||||||
|
func RunLoginDelete(ctx *cli.Context) error {
|
||||||
|
logins, err := config.GetLogins()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var name string
|
||||||
|
|
||||||
|
if len(ctx.Args().First()) != 0 {
|
||||||
|
name = ctx.Args().First()
|
||||||
|
} else if len(logins) == 1 {
|
||||||
|
name = logins[0].Name
|
||||||
|
} else {
|
||||||
|
return errors.New("Please specify a login name")
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.DeleteLogin(name)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package login
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
|
||||||
|
"github.com/skratchdot/open-golang/open"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLoginEdit represents to login a gitea server.
|
||||||
|
var CmdLoginEdit = cli.Command{
|
||||||
|
Name: "edit",
|
||||||
|
Aliases: []string{"e"},
|
||||||
|
Usage: "Edit Gitea logins",
|
||||||
|
Description: `Edit Gitea logins`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runLoginEdit,
|
||||||
|
Flags: []cli.Flag{&flags.OutputFlag},
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLoginEdit(_ *cli.Context) error {
|
||||||
|
return open.Start(config.GetConfigPath())
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package login
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLoginHelper represents to login a gitea helper.
|
||||||
|
var CmdLoginHelper = cli.Command{
|
||||||
|
Name: "helper",
|
||||||
|
Aliases: []string{"git-credential"},
|
||||||
|
Usage: "Git helper",
|
||||||
|
Description: `Git helper`,
|
||||||
|
Hidden: true,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
{
|
||||||
|
Name: "store",
|
||||||
|
Description: "Command drops",
|
||||||
|
Aliases: []string{"erase"},
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "setup",
|
||||||
|
Description: "Setup helper to tea authenticate",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
logins, err := config.GetLogins()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, login := range logins {
|
||||||
|
added, err := task.SetupHelper(login)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if added {
|
||||||
|
fmt.Printf("Added \"%s\"\n", login.Name)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\"%s\" has already been added!\n", login.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "get",
|
||||||
|
Description: "Get token to auth",
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
wants := map[string]string{}
|
||||||
|
s := bufio.NewScanner(os.Stdin)
|
||||||
|
for s.Scan() {
|
||||||
|
line := s.Text()
|
||||||
|
if line == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, "=", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key, value := parts[0], parts[1]
|
||||||
|
if key == "url" {
|
||||||
|
u, err := url.Parse(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wants["protocol"] = u.Scheme
|
||||||
|
wants["host"] = u.Host
|
||||||
|
wants["path"] = u.Path
|
||||||
|
wants["username"] = u.User.Username()
|
||||||
|
wants["password"], _ = u.User.Password()
|
||||||
|
} else {
|
||||||
|
wants[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wants["host"]) == 0 {
|
||||||
|
log.Fatal("Require hostname")
|
||||||
|
} else if len(wants["protocol"]) == 0 {
|
||||||
|
wants["protocol"] = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
userConfig := config.GetLoginByHost(wants["host"])
|
||||||
|
if len(userConfig.Token) == 0 {
|
||||||
|
log.Fatal("User no set")
|
||||||
|
}
|
||||||
|
|
||||||
|
host, err := url.Parse(userConfig.URL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fmt.Fprintf(os.Stdout, "protocol=%s\nhost=%s\nusername=%s\npassword=%s\n", host.Scheme, host.Host, userConfig.User, userConfig.Token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package login
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdLoginList represents to login a gitea server.
|
||||||
|
var CmdLoginList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List Gitea logins",
|
||||||
|
Description: `List Gitea logins`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunLoginList,
|
||||||
|
Flags: []cli.Flag{&flags.OutputFlag},
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunLoginList list all logins
|
||||||
|
func RunLoginList(cmd *cli.Context) error {
|
||||||
|
logins, err := config.GetLogins()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.LoginsList(logins, cmd.String("output"))
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,60 +1,20 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"code.gitea.io/tea/cmd/login"
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdLogout represents to logout a gitea server.
|
// CmdLogout represents to logout a gitea server.
|
||||||
var CmdLogout = cli.Command{
|
var CmdLogout = cli.Command{
|
||||||
Name: "logout",
|
Name: "logout",
|
||||||
|
Category: catSetup,
|
||||||
Usage: "Log out from a Gitea server",
|
Usage: "Log out from a Gitea server",
|
||||||
Description: `Log out from a Gitea server`,
|
Description: `Log out from a Gitea server`,
|
||||||
Action: runLogout,
|
ArgsUsage: "<login name>",
|
||||||
Flags: []cli.Flag{
|
Action: login.RunLoginDelete,
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name, n",
|
|
||||||
Usage: "Login name to remove",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runLogout(ctx *cli.Context) error {
|
|
||||||
var name string
|
|
||||||
if len(os.Args) == 3 {
|
|
||||||
name = os.Args[2]
|
|
||||||
} else if ctx.IsSet("name") {
|
|
||||||
name = ctx.String("name")
|
|
||||||
} else {
|
|
||||||
return errors.New("Please specify a login name")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := loadConfig(yamlConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to load config file " + yamlConfigPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
var idx = -1
|
|
||||||
for i, l := range config.Logins {
|
|
||||||
if l.Name == name {
|
|
||||||
idx = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if idx > -1 {
|
|
||||||
config.Logins = append(config.Logins[:idx], config.Logins[idx+1:]...)
|
|
||||||
err = saveConfig(yamlConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to save config file " + yamlConfigPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/milestones"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdMilestones represents to operate repositories milestones.
|
||||||
|
var CmdMilestones = cli.Command{
|
||||||
|
Name: "milestones",
|
||||||
|
Aliases: []string{"milestone", "ms"},
|
||||||
|
Category: catEntities,
|
||||||
|
Usage: "List and create milestones",
|
||||||
|
Description: `List and create milestones`,
|
||||||
|
ArgsUsage: "[<milestone name>]",
|
||||||
|
Action: runMilestones,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&milestones.CmdMilestonesList,
|
||||||
|
&milestones.CmdMilestonesCreate,
|
||||||
|
&milestones.CmdMilestonesClose,
|
||||||
|
&milestones.CmdMilestonesDelete,
|
||||||
|
&milestones.CmdMilestonesReopen,
|
||||||
|
&milestones.CmdMilestonesIssues,
|
||||||
|
},
|
||||||
|
Flags: milestones.CmdMilestonesList.Flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMilestones(ctx *cli.Context) error {
|
||||||
|
if ctx.Args().Len() == 1 {
|
||||||
|
return runMilestoneDetail(ctx, ctx.Args().First())
|
||||||
|
}
|
||||||
|
return milestones.RunMilestonesList(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMilestoneDetail(cmd *cli.Context, name string) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
milestone, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.MilestoneDetails(milestone)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package milestones
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdMilestonesClose represents a sub command of milestones to close an milestone
|
||||||
|
var CmdMilestonesClose = cli.Command{
|
||||||
|
Name: "close",
|
||||||
|
Usage: "Change state of one or more milestones to 'closed'",
|
||||||
|
Description: `Change state of one or more milestones to 'closed'`,
|
||||||
|
ArgsUsage: "<milestone name> [<milestone name>...]",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
if ctx.Bool("force") {
|
||||||
|
return deleteMilestone(ctx)
|
||||||
|
}
|
||||||
|
return editMilestoneStatus(ctx, true)
|
||||||
|
},
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "force",
|
||||||
|
Aliases: []string{"f"},
|
||||||
|
Usage: "delete milestone",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package milestones
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/araddon/dateparse"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdMilestonesCreate represents a sub command of milestones to create milestone
|
||||||
|
var CmdMilestonesCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create an milestone on repository",
|
||||||
|
Description: `Create an milestone on repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runMilestonesCreate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "title",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Usage: "milestone title to create",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Usage: "milestone description to create",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "deadline",
|
||||||
|
Aliases: []string{"expires", "x"},
|
||||||
|
Usage: "set milestone deadline (default is no due date)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "state",
|
||||||
|
Usage: "set milestone state (default is open)",
|
||||||
|
DefaultText: "open",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMilestonesCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
date := ctx.String("deadline")
|
||||||
|
deadline := &time.Time{}
|
||||||
|
if date != "" {
|
||||||
|
t, err := dateparse.ParseAny(date)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
deadline = &t
|
||||||
|
}
|
||||||
|
|
||||||
|
state := gitea.StateOpen
|
||||||
|
if ctx.String("state") == "closed" {
|
||||||
|
state = gitea.StateClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.NumFlags() == 0 {
|
||||||
|
return interact.CreateMilestone(ctx.Login, ctx.Owner, ctx.Repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.CreateMilestone(
|
||||||
|
ctx.Login,
|
||||||
|
ctx.Owner,
|
||||||
|
ctx.Repo,
|
||||||
|
ctx.String("title"),
|
||||||
|
ctx.String("description"),
|
||||||
|
deadline,
|
||||||
|
state,
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package milestones
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdMilestonesDelete represents a sub command of milestones to delete an milestone
|
||||||
|
var CmdMilestonesDelete = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "delete a milestone",
|
||||||
|
Description: "delete a milestone",
|
||||||
|
ArgsUsage: "<milestone name>",
|
||||||
|
Action: deleteMilestone,
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteMilestone(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
_, err := client.DeleteMilestoneByName(ctx.Owner, ctx.Repo, ctx.Args().First())
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package milestones
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var msIssuesFieldsFlag = flags.FieldsFlag(print.IssueFields, []string{
|
||||||
|
"index", "kind", "title", "state", "updated", "labels",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdMilestonesIssues represents a sub command of milestones to manage issue/pull of an milestone
|
||||||
|
var CmdMilestonesIssues = cli.Command{
|
||||||
|
Name: "issues",
|
||||||
|
Aliases: []string{"i"},
|
||||||
|
Usage: "manage issue/pull of an milestone",
|
||||||
|
Description: "manage issue/pull of an milestone",
|
||||||
|
ArgsUsage: "<milestone name>",
|
||||||
|
Action: runMilestoneIssueList,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&CmdMilestoneAddIssue,
|
||||||
|
&CmdMilestoneRemoveIssue,
|
||||||
|
},
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "state",
|
||||||
|
Usage: "Filter by issue state (all|open|closed)",
|
||||||
|
DefaultText: "open",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "kind",
|
||||||
|
Usage: "Filter by kind (issue|pull)",
|
||||||
|
},
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
msIssuesFieldsFlag,
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdMilestoneAddIssue represents a sub command of milestone issues to add an issue/pull to an milestone
|
||||||
|
var CmdMilestoneAddIssue = cli.Command{
|
||||||
|
Name: "add",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "Add an issue/pull to an milestone",
|
||||||
|
Description: "Add an issue/pull to an milestone",
|
||||||
|
ArgsUsage: "<milestone name> <issue/pull index>",
|
||||||
|
Action: runMilestoneIssueAdd,
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdMilestoneRemoveIssue represents a sub command of milestones to remove an issue/pull from an milestone
|
||||||
|
var CmdMilestoneRemoveIssue = cli.Command{
|
||||||
|
Name: "remove",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "Remove an issue/pull to an milestone",
|
||||||
|
Description: "Remove an issue/pull to an milestone",
|
||||||
|
ArgsUsage: "<milestone name> <issue/pull index>",
|
||||||
|
Action: runMilestoneIssueRemove,
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMilestoneIssueList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
state := gitea.StateOpen
|
||||||
|
switch ctx.String("state") {
|
||||||
|
case "all":
|
||||||
|
state = gitea.StateAll
|
||||||
|
case "closed":
|
||||||
|
state = gitea.StateClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := gitea.IssueTypeAll
|
||||||
|
switch ctx.String("kind") {
|
||||||
|
case "issue":
|
||||||
|
kind = gitea.IssueTypeIssue
|
||||||
|
case "pull":
|
||||||
|
kind = gitea.IssueTypePull
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Args().Len() != 1 {
|
||||||
|
return fmt.Errorf("Must specify milestone name")
|
||||||
|
}
|
||||||
|
|
||||||
|
milestone := ctx.Args().First()
|
||||||
|
// make sure milestone exist
|
||||||
|
_, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestone)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
issues, _, err := client.ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
Milestones: []string{milestone},
|
||||||
|
Type: kind,
|
||||||
|
State: state,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := msIssuesFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.IssuesPullsList(issues, ctx.Output, fields)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMilestoneIssueAdd(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
if ctx.Args().Len() != 2 {
|
||||||
|
return fmt.Errorf("need two arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
mileName := ctx.Args().Get(0)
|
||||||
|
issueIndex := ctx.Args().Get(1)
|
||||||
|
idx, err := utils.ArgToIndex(issueIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure milestone exist
|
||||||
|
mile, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, mileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = client.EditIssue(ctx.Owner, ctx.Repo, idx, gitea.EditIssueOption{
|
||||||
|
Milestone: &mile.ID,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func runMilestoneIssueRemove(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
if ctx.Args().Len() != 2 {
|
||||||
|
return fmt.Errorf("need two arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
mileName := ctx.Args().Get(0)
|
||||||
|
issueIndex := ctx.Args().Get(1)
|
||||||
|
idx, err := utils.ArgToIndex(issueIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
issue, _, err := client.GetIssue(ctx.Owner, ctx.Repo, idx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.Milestone == nil {
|
||||||
|
return fmt.Errorf("issue is not assigned to a milestone")
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.Milestone.Title != mileName {
|
||||||
|
return fmt.Errorf("issue is not assigned to this milestone")
|
||||||
|
}
|
||||||
|
|
||||||
|
zero := int64(0)
|
||||||
|
_, _, err = client.EditIssue(ctx.Owner, ctx.Repo, idx, gitea.EditIssueOption{
|
||||||
|
Milestone: &zero,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package milestones
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fieldsFlag = flags.FieldsFlag(print.MilestoneFields, []string{
|
||||||
|
"title", "items", "duedate",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdMilestonesList represents a sub command of milestones to list milestones
|
||||||
|
var CmdMilestonesList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List milestones of the repository",
|
||||||
|
Description: `List milestones of the repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunMilestonesList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
fieldsFlag,
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "state",
|
||||||
|
Usage: "Filter by milestone state (all|open|closed)",
|
||||||
|
DefaultText: "open",
|
||||||
|
},
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunMilestonesList list milestones
|
||||||
|
func RunMilestonesList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
fields, err := fieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
state := gitea.StateOpen
|
||||||
|
switch ctx.String("state") {
|
||||||
|
case "all":
|
||||||
|
state = gitea.StateAll
|
||||||
|
if !cmd.IsSet("fields") { // add to default fields
|
||||||
|
fields = append(fields, "state")
|
||||||
|
}
|
||||||
|
case "closed":
|
||||||
|
state = gitea.StateClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
milestones, _, err := client.ListRepoMilestones(ctx.Owner, ctx.Repo, gitea.ListMilestoneOption{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
State: state,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.MilestonesList(milestones, ctx.Output, fields)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package milestones
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdMilestonesReopen represents a sub command of milestones to open an milestone
|
||||||
|
var CmdMilestonesReopen = cli.Command{
|
||||||
|
Name: "reopen",
|
||||||
|
Aliases: []string{"open"},
|
||||||
|
Usage: "Change state of one or more milestones to 'open'",
|
||||||
|
Description: `Change state of one or more milestones to 'open'`,
|
||||||
|
ArgsUsage: "<milestone name> [<milestone name> ...]",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
return editMilestoneStatus(ctx, false)
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func editMilestoneStatus(cmd *cli.Context, close bool) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
if ctx.Args().Len() == 0 {
|
||||||
|
return fmt.Errorf(ctx.Command.ArgsUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
state := gitea.StateOpen
|
||||||
|
if close {
|
||||||
|
state = gitea.StateClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
for _, ms := range ctx.Args().Slice() {
|
||||||
|
opts := gitea.EditMilestoneOption{
|
||||||
|
State: &state,
|
||||||
|
Title: ms,
|
||||||
|
}
|
||||||
|
milestone, _, err := client.EditMilestoneByName(ctx.Owner, ctx.Repo, ms, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Args().Len() > 1 {
|
||||||
|
fmt.Printf("%s/milestone/%d\n", ctx.GetRemoteRepoHTMLURL(), milestone.ID)
|
||||||
|
} else {
|
||||||
|
print.MilestoneDetails(milestone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/notifications"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdNotifications is the main command to operate with notifications
|
||||||
|
var CmdNotifications = cli.Command{
|
||||||
|
Name: "notifications",
|
||||||
|
Aliases: []string{"notification", "n"},
|
||||||
|
Category: catHelpers,
|
||||||
|
Usage: "Show notifications",
|
||||||
|
Description: "Show notifications, by default based on the current repo if available",
|
||||||
|
Action: notifications.RunNotificationsList,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
¬ifications.CmdNotificationsList,
|
||||||
|
¬ifications.CmdNotificationsMarkRead,
|
||||||
|
¬ifications.CmdNotificationsMarkUnread,
|
||||||
|
¬ifications.CmdNotificationsMarkPinned,
|
||||||
|
¬ifications.CmdNotificationsUnpin,
|
||||||
|
},
|
||||||
|
Flags: notifications.CmdNotificationsList.Flags,
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package notifications
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var notifyFieldsFlag = flags.FieldsFlag(print.NotificationFields, []string{
|
||||||
|
"id", "status", "index", "type", "state", "title",
|
||||||
|
})
|
||||||
|
|
||||||
|
var notifyTypeFlag = flags.NewCsvFlag("types", "subject types to filter by", []string{"t"},
|
||||||
|
[]string{"issue", "pull", "repository", "commit"}, nil)
|
||||||
|
|
||||||
|
// CmdNotificationsList represents a sub command of notifications to list notifications
|
||||||
|
var CmdNotificationsList = cli.Command{
|
||||||
|
Name: "ls",
|
||||||
|
Aliases: []string{"list"},
|
||||||
|
Usage: "List notifications",
|
||||||
|
Description: `List notifications`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunNotificationsList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
notifyFieldsFlag,
|
||||||
|
notifyTypeFlag,
|
||||||
|
}, flags.NotificationFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunNotificationsList list notifications
|
||||||
|
func RunNotificationsList(ctx *cli.Context) error {
|
||||||
|
var states []gitea.NotifyStatus
|
||||||
|
statesStr, err := flags.NotificationStateFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, s := range statesStr {
|
||||||
|
states = append(states, gitea.NotifyStatus(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
var types []gitea.NotifySubjectType
|
||||||
|
typesStr, err := notifyTypeFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, t := range typesStr {
|
||||||
|
types = append(types, gitea.NotifySubjectType(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
return listNotifications(ctx, states, types)
|
||||||
|
}
|
||||||
|
|
||||||
|
// listNotifications will get the notifications based on status and subject type
|
||||||
|
func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects []gitea.NotifySubjectType) error {
|
||||||
|
var news []*gitea.NotificationThread
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
all := ctx.Bool("mine")
|
||||||
|
|
||||||
|
// This enforces pagination (see https://github.com/go-gitea/gitea/issues/16733)
|
||||||
|
listOpts := ctx.GetListOptions()
|
||||||
|
if listOpts.Page == 0 {
|
||||||
|
listOpts.Page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := notifyFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if all {
|
||||||
|
// add repository to the default fields
|
||||||
|
if !cmd.IsSet("fields") {
|
||||||
|
fields = append(fields, "repository")
|
||||||
|
}
|
||||||
|
|
||||||
|
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
|
||||||
|
ListOptions: listOpts,
|
||||||
|
Status: status,
|
||||||
|
SubjectTypes: subjects,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
news, _, err = client.ListRepoNotifications(ctx.Owner, ctx.Repo, gitea.ListNotificationOptions{
|
||||||
|
ListOptions: listOpts,
|
||||||
|
Status: status,
|
||||||
|
SubjectTypes: subjects,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
print.NotificationsList(news, ctx.Output, fields)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package notifications
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdNotificationsMarkRead represents a sub command of notifications to list read notifications
|
||||||
|
var CmdNotificationsMarkRead = cli.Command{
|
||||||
|
Name: "read",
|
||||||
|
Aliases: []string{"r"},
|
||||||
|
Usage: "Mark all filtered or a specific notification as read",
|
||||||
|
Description: "Mark all filtered or a specific notification as read",
|
||||||
|
ArgsUsage: "[all | <notification id>]",
|
||||||
|
Flags: flags.NotificationFlags,
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
cmd := context.InitCommand(ctx)
|
||||||
|
filter, err := flags.NotificationStateFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !cmd.IsSet(flags.NotificationStateFlag.Name) {
|
||||||
|
filter = []string{string(gitea.NotifyStatusUnread)}
|
||||||
|
}
|
||||||
|
return markNotificationAs(cmd, filter, gitea.NotifyStatusRead)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdNotificationsMarkUnread will mark notifications as unread.
|
||||||
|
var CmdNotificationsMarkUnread = cli.Command{
|
||||||
|
Name: "unread",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Usage: "Mark all filtered or a specific notification as unread",
|
||||||
|
Description: "Mark all filtered or a specific notification as unread",
|
||||||
|
ArgsUsage: "[all | <notification id>]",
|
||||||
|
Flags: flags.NotificationFlags,
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
cmd := context.InitCommand(ctx)
|
||||||
|
filter, err := flags.NotificationStateFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !cmd.IsSet(flags.NotificationStateFlag.Name) {
|
||||||
|
filter = []string{string(gitea.NotifyStatusRead)}
|
||||||
|
}
|
||||||
|
return markNotificationAs(cmd, filter, gitea.NotifyStatusUnread)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdNotificationsMarkPinned will mark notifications as unread.
|
||||||
|
var CmdNotificationsMarkPinned = cli.Command{
|
||||||
|
Name: "pin",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "Mark all filtered or a specific notification as pinned",
|
||||||
|
Description: "Mark all filtered or a specific notification as pinned",
|
||||||
|
ArgsUsage: "[all | <notification id>]",
|
||||||
|
Flags: flags.NotificationFlags,
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
cmd := context.InitCommand(ctx)
|
||||||
|
filter, err := flags.NotificationStateFlag.GetValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !cmd.IsSet(flags.NotificationStateFlag.Name) {
|
||||||
|
filter = []string{string(gitea.NotifyStatusUnread)}
|
||||||
|
}
|
||||||
|
return markNotificationAs(cmd, filter, gitea.NotifyStatusPinned)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdNotificationsUnpin will mark pinned notifications as unread.
|
||||||
|
var CmdNotificationsUnpin = cli.Command{
|
||||||
|
Name: "unpin",
|
||||||
|
Usage: "Unpin all pinned or a specific notification",
|
||||||
|
Description: "Marks all pinned or a specific notification as read",
|
||||||
|
ArgsUsage: "[all | <notification id>]",
|
||||||
|
Flags: flags.NotificationFlags,
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
cmd := context.InitCommand(ctx)
|
||||||
|
filter := []string{string(gitea.NotifyStatusPinned)}
|
||||||
|
// NOTE: we implicitly mark it as read, to match web UI semantics. marking as unread might be more useful?
|
||||||
|
return markNotificationAs(cmd, filter, gitea.NotifyStatusRead)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func markNotificationAs(cmd *context.TeaContext, filterStates []string, targetState gitea.NotifyStatus) (err error) {
|
||||||
|
client := cmd.Login.Client()
|
||||||
|
subject := cmd.Args().First()
|
||||||
|
allRepos := cmd.Bool("mine")
|
||||||
|
|
||||||
|
states := []gitea.NotifyStatus{}
|
||||||
|
for _, s := range filterStates {
|
||||||
|
states = append(states, gitea.NotifyStatus(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch subject {
|
||||||
|
case "", "all":
|
||||||
|
opts := gitea.MarkNotificationOptions{Status: states, ToStatus: targetState}
|
||||||
|
|
||||||
|
if allRepos {
|
||||||
|
_, _, err = client.ReadNotifications(opts)
|
||||||
|
} else {
|
||||||
|
cmd.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
_, _, err = client.ReadRepoNotifications(cmd.Owner, cmd.Repo, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: print all affected notification subject URLs
|
||||||
|
// (not supported by API currently, https://github.com/go-gitea/gitea/issues/16797)
|
||||||
|
|
||||||
|
default:
|
||||||
|
id, err := utils.ArgToIndex(subject)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _, err = client.ReadNotification(id, targetState)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, _, err := client.GetNotification(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// FIXME: this is an API URL, we want to display a web ui link..
|
||||||
|
fmt.Println(n.Subject.URL)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
local_git "code.gitea.io/tea/modules/git"
|
||||||
|
|
||||||
|
"github.com/skratchdot/open-golang/open"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdOpen represents a sub command of issues to open issue on the web browser
|
||||||
|
var CmdOpen = cli.Command{
|
||||||
|
Name: "open",
|
||||||
|
Aliases: []string{"o"},
|
||||||
|
Category: catHelpers,
|
||||||
|
Usage: "Open something of the repository in web browser",
|
||||||
|
Description: `Open something of the repository in web browser`,
|
||||||
|
Action: runOpen,
|
||||||
|
Flags: append([]cli.Flag{}, flags.LoginRepoFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runOpen(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
var suffix string
|
||||||
|
number := ctx.Args().Get(0)
|
||||||
|
switch {
|
||||||
|
case strings.EqualFold(number, "issues"):
|
||||||
|
suffix = "issues"
|
||||||
|
case strings.EqualFold(number, "pulls"):
|
||||||
|
suffix = "pulls"
|
||||||
|
case strings.EqualFold(number, "releases"):
|
||||||
|
suffix = "releases"
|
||||||
|
case strings.EqualFold(number, "commits"):
|
||||||
|
repo, err := local_git.RepoForWorkdir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err := repo.Head()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := b.Name()
|
||||||
|
switch {
|
||||||
|
case name.IsBranch():
|
||||||
|
suffix = "commits/branch/" + name.Short()
|
||||||
|
case name.IsTag():
|
||||||
|
suffix = "commits/tag/" + name.Short()
|
||||||
|
}
|
||||||
|
case strings.EqualFold(number, "branches"):
|
||||||
|
suffix = "branches"
|
||||||
|
case strings.EqualFold(number, "wiki"):
|
||||||
|
suffix = "wiki"
|
||||||
|
case strings.EqualFold(number, "activity"):
|
||||||
|
suffix = "activity"
|
||||||
|
case strings.EqualFold(number, "settings"):
|
||||||
|
suffix = "settings"
|
||||||
|
case strings.EqualFold(number, "labels"):
|
||||||
|
suffix = "labels"
|
||||||
|
case strings.EqualFold(number, "milestones"):
|
||||||
|
suffix = "milestones"
|
||||||
|
case number != "":
|
||||||
|
suffix = "issues/" + number
|
||||||
|
default:
|
||||||
|
suffix = number
|
||||||
|
}
|
||||||
|
|
||||||
|
return open.Run(path.Join(ctx.GetRemoteRepoHTMLURL(), suffix))
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/organizations"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdOrgs represents handle organization
|
||||||
|
var CmdOrgs = cli.Command{
|
||||||
|
Name: "organizations",
|
||||||
|
Aliases: []string{"organization", "org"},
|
||||||
|
Category: catEntities,
|
||||||
|
Usage: "List, create, delete organizations",
|
||||||
|
Description: "Show organization details",
|
||||||
|
ArgsUsage: "[<organization>]",
|
||||||
|
Action: runOrganizations,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&organizations.CmdOrganizationList,
|
||||||
|
&organizations.CmdOrganizationCreate,
|
||||||
|
&organizations.CmdOrganizationDelete,
|
||||||
|
},
|
||||||
|
Flags: organizations.CmdOrganizationList.Flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runOrganizations(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
if ctx.Args().Len() == 1 {
|
||||||
|
return runOrganizationDetail(ctx)
|
||||||
|
}
|
||||||
|
return organizations.RunOrganizationList(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runOrganizationDetail(ctx *context.TeaContext) error {
|
||||||
|
org, _, err := ctx.Login.Client().GetOrg(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.OrganizationDetails(org)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package organizations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdOrganizationCreate represents a sub command of organizations to delete a given user organization
|
||||||
|
var CmdOrganizationCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create an organization",
|
||||||
|
Description: "Create an organization",
|
||||||
|
Action: RunOrganizationCreate,
|
||||||
|
ArgsUsage: "<organization name>",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "website",
|
||||||
|
Aliases: []string{"w"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "location",
|
||||||
|
Aliases: []string{"L"},
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "visibility",
|
||||||
|
Aliases: []string{"v"},
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "repo-admins-can-change-team-access",
|
||||||
|
},
|
||||||
|
&flags.LoginFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunOrganizationCreate sets up a new organization
|
||||||
|
func RunOrganizationCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
if ctx.Args().Len() < 1 {
|
||||||
|
return fmt.Errorf("You have to specify the organization name you want to create")
|
||||||
|
}
|
||||||
|
|
||||||
|
var visibility gitea.VisibleType
|
||||||
|
switch ctx.String("visibility") {
|
||||||
|
case "", "public":
|
||||||
|
visibility = gitea.VisibleTypePublic
|
||||||
|
case "private":
|
||||||
|
visibility = gitea.VisibleTypePrivate
|
||||||
|
case "limited":
|
||||||
|
visibility = gitea.VisibleTypeLimited
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown visibility '%s'", ctx.String("visibility"))
|
||||||
|
}
|
||||||
|
|
||||||
|
org, _, err := ctx.Login.Client().CreateOrg(gitea.CreateOrgOption{
|
||||||
|
Name: ctx.Args().First(),
|
||||||
|
// FullName: , // not really meaningful for orgs (not displayed in webui, use description instead?)
|
||||||
|
Description: ctx.String("description"),
|
||||||
|
Website: ctx.String("website"),
|
||||||
|
Location: ctx.String("location"),
|
||||||
|
RepoAdminChangeTeamAccess: ctx.Bool("repo-admins-can-change-team-access"),
|
||||||
|
Visibility: visibility,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.OrganizationDetails(org)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package organizations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdOrganizationDelete represents a sub command of organizations to delete a given user organization
|
||||||
|
var CmdOrganizationDelete = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "Delete users Organizations",
|
||||||
|
Description: "Delete users organizations",
|
||||||
|
ArgsUsage: "<organization name>",
|
||||||
|
Action: RunOrganizationDelete,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&flags.LoginFlag,
|
||||||
|
&flags.RemoteFlag,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunOrganizationDelete delete user organization
|
||||||
|
func RunOrganizationDelete(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
if ctx.Args().Len() < 1 {
|
||||||
|
return fmt.Errorf("You have to specify the organization name you want to delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := client.DeleteOrg(ctx.Args().First())
|
||||||
|
if response != nil && response.StatusCode == 404 {
|
||||||
|
return fmt.Errorf("The given organization does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package organizations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdOrganizationList represents a sub command of organizations to list users organizations
|
||||||
|
var CmdOrganizationList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List Organizations",
|
||||||
|
Description: "List users organizations",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunOrganizationList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunOrganizationList list user organizations
|
||||||
|
func RunOrganizationList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
userOrganizations, _, err := client.ListUserOrgs(ctx.Login.User, gitea.ListOrgsOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.OrganizationsList(userOrganizations, ctx.Output)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
109
cmd/pulls.go
109
cmd/pulls.go
|
@ -1,72 +1,95 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
|
"code.gitea.io/tea/cmd/pulls"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
"code.gitea.io/tea/modules/workaround"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdPulls represents to login a gitea server.
|
// CmdPulls is the main command to operate on PRs
|
||||||
var CmdPulls = cli.Command{
|
var CmdPulls = cli.Command{
|
||||||
Name: "pulls",
|
Name: "pulls",
|
||||||
Usage: "List open pull requests",
|
Aliases: []string{"pull", "pr"},
|
||||||
Description: `List open pull requests`,
|
Category: catEntities,
|
||||||
|
Usage: "Manage and checkout pull requests",
|
||||||
|
Description: `Lists PRs when called without argument. If PR index is provided, will show it in detail.`,
|
||||||
|
ArgsUsage: "[<pull index>]",
|
||||||
Action: runPulls,
|
Action: runPulls,
|
||||||
Flags: AllDefaultFlags,
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "comments",
|
||||||
|
Usage: "Whether to display comments (will prompt if not provided & run interactively)",
|
||||||
|
},
|
||||||
|
}, pulls.CmdPullsList.Flags...),
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&pulls.CmdPullsList,
|
||||||
|
&pulls.CmdPullsCheckout,
|
||||||
|
&pulls.CmdPullsClean,
|
||||||
|
&pulls.CmdPullsCreate,
|
||||||
|
&pulls.CmdPullsClose,
|
||||||
|
&pulls.CmdPullsReopen,
|
||||||
|
&pulls.CmdPullsReview,
|
||||||
|
&pulls.CmdPullsApprove,
|
||||||
|
&pulls.CmdPullsReject,
|
||||||
|
&pulls.CmdPullsMerge,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runPulls(ctx *cli.Context) error {
|
func runPulls(ctx *cli.Context) error {
|
||||||
login, owner, repo := initCommand()
|
if ctx.Args().Len() == 1 {
|
||||||
|
return runPullDetail(ctx, ctx.Args().First())
|
||||||
prs, err := login.Client().ListRepoPullRequests(owner, repo, gitea.ListPullRequestsOptions{
|
}
|
||||||
Page: 0,
|
return pulls.RunPullsList(ctx)
|
||||||
State: string(gitea.StateOpen),
|
}
|
||||||
})
|
|
||||||
|
|
||||||
|
func runPullDetail(cmd *cli.Context, index string) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
idx, err := utils.ArgToIndex(index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
headers := []string{
|
client := ctx.Login.Client()
|
||||||
"Index",
|
pr, _, err := client.GetPullRequest(ctx.Owner, ctx.Repo, idx)
|
||||||
"Name",
|
if err != nil {
|
||||||
"Updated",
|
return err
|
||||||
"Title",
|
}
|
||||||
|
if err := workaround.FixPullHeadSha(client, pr); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var values [][]string
|
reviews, _, err := client.ListPullReviews(ctx.Owner, ctx.Repo, idx, gitea.ListPullReviewsOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
if len(prs) == 0 {
|
})
|
||||||
Output(outputValue, headers, values)
|
if err != nil {
|
||||||
return nil
|
fmt.Printf("error while loading reviews: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pr := range prs {
|
ci, _, err := client.GetCombinedStatus(ctx.Owner, ctx.Repo, pr.Head.Sha)
|
||||||
if pr == nil {
|
if err != nil {
|
||||||
continue
|
fmt.Printf("error while loading CI: %v\n", err)
|
||||||
}
|
}
|
||||||
name := pr.Poster.FullName
|
|
||||||
if len(name) == 0 {
|
print.PullDetails(pr, reviews, ci)
|
||||||
name = pr.Poster.UserName
|
|
||||||
|
if pr.Comments > 0 {
|
||||||
|
err = interact.ShowCommentsMaybeInteractive(ctx, idx, pr.Comments)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("error loading comments: %v\n", err)
|
||||||
}
|
}
|
||||||
values = append(
|
|
||||||
values,
|
|
||||||
[]string{
|
|
||||||
strconv.FormatInt(pr.Index, 10),
|
|
||||||
name,
|
|
||||||
pr.Updated.Format("2006-01-02 15:04:05"),
|
|
||||||
pr.Title,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Output(outputValue, headers, values)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsApprove approves a PR
|
||||||
|
var CmdPullsApprove = cli.Command{
|
||||||
|
Name: "approve",
|
||||||
|
Aliases: []string{"lgtm", "a"},
|
||||||
|
Usage: "Approve a pull request",
|
||||||
|
Description: "Approve a pull request",
|
||||||
|
ArgsUsage: "<pull index> [<comment>]",
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
if ctx.Args().Len() == 0 {
|
||||||
|
return fmt.Errorf("Must specify a PR index")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
comment := strings.Join(ctx.Args().Tail(), " ")
|
||||||
|
|
||||||
|
return task.CreatePullReview(ctx, idx, gitea.ReviewStateApproved, comment, nil)
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsCheckout is a command to locally checkout the given PR
|
||||||
|
var CmdPullsCheckout = cli.Command{
|
||||||
|
Name: "checkout",
|
||||||
|
Aliases: []string{"co"},
|
||||||
|
Usage: "Locally check out the given PR",
|
||||||
|
Description: `Locally check out the given PR`,
|
||||||
|
Action: runPullsCheckout,
|
||||||
|
ArgsUsage: "<pull index>",
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "branch",
|
||||||
|
Aliases: []string{"b"},
|
||||||
|
Usage: "Create a local branch if it doesn't exist yet",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPullsCheckout(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{
|
||||||
|
LocalRepo: true,
|
||||||
|
RemoteRepo: true,
|
||||||
|
})
|
||||||
|
if ctx.Args().Len() != 1 {
|
||||||
|
return fmt.Errorf("Must specify a PR index")
|
||||||
|
}
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.PullCheckout(ctx.Login, ctx.Owner, ctx.Repo, ctx.Bool("branch"), idx, interact.PromptPassword)
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsClean removes the remote and local feature branches, if a PR is merged.
|
||||||
|
var CmdPullsClean = cli.Command{
|
||||||
|
Name: "clean",
|
||||||
|
Usage: "Deletes local & remote feature-branches for a closed pull request",
|
||||||
|
Description: `Deletes local & remote feature-branches for a closed pull request`,
|
||||||
|
ArgsUsage: "<pull index>",
|
||||||
|
Action: runPullsClean,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "ignore-sha",
|
||||||
|
Usage: "Find the local branch by name instead of commit hash (less precise)",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPullsClean(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{LocalRepo: true})
|
||||||
|
if ctx.Args().Len() != 1 {
|
||||||
|
return fmt.Errorf("Must specify a PR index")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.PullClean(ctx.Login, ctx.Owner, ctx.Repo, idx, ctx.Bool("ignore-sha"), interact.PromptPassword)
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsClose closes a given open pull request
|
||||||
|
var CmdPullsClose = cli.Command{
|
||||||
|
Name: "close",
|
||||||
|
Usage: "Change state of one or more pull requests to 'closed'",
|
||||||
|
Description: `Change state of one or more pull requests to 'closed'`,
|
||||||
|
ArgsUsage: "<pull index> [<pull index>...]",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
var s = gitea.StateClosed
|
||||||
|
return editPullState(ctx, gitea.EditPullRequestOption{State: &s})
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsCreate creates a pull request
|
||||||
|
var CmdPullsCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create a pull-request",
|
||||||
|
Description: "Create a pull-request in the current repo",
|
||||||
|
Action: runPullsCreate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "head",
|
||||||
|
Usage: "Branch name of the PR source (default is current one). To specify a different head repo, use <user>:<branch>",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "base",
|
||||||
|
Aliases: []string{"b"},
|
||||||
|
Usage: "Branch name of the PR target (default is repos default branch)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "allow-maintainer-edits",
|
||||||
|
Aliases: []string{"edits"},
|
||||||
|
Usage: "Enable maintainers to push to the base branch of created pull",
|
||||||
|
Value: true,
|
||||||
|
},
|
||||||
|
}, flags.IssuePRCreateFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPullsCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
// no args -> interactive mode
|
||||||
|
if ctx.NumFlags() == 0 {
|
||||||
|
return interact.CreatePull(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// else use args to create PR
|
||||||
|
opts, err := flags.GetIssuePRCreateFlags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.CreatePull(
|
||||||
|
ctx,
|
||||||
|
ctx.String("base"),
|
||||||
|
ctx.String("head"),
|
||||||
|
ctx.Bool("allow-maintainer-edits"),
|
||||||
|
opts,
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// editPullState abstracts the arg parsing to edit the given pull request
|
||||||
|
func editPullState(cmd *cli.Context, opts gitea.EditPullRequestOption) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
if ctx.Args().Len() == 0 {
|
||||||
|
return fmt.Errorf("Please provide a Pull Request index")
|
||||||
|
}
|
||||||
|
|
||||||
|
indices, err := utils.ArgsToIndices(ctx.Args().Slice())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
for _, index := range indices {
|
||||||
|
pr, _, err := client.EditPullRequest(ctx.Owner, ctx.Repo, index, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(indices) > 1 {
|
||||||
|
fmt.Println(pr.HTMLURL)
|
||||||
|
} else {
|
||||||
|
print.PullDetails(pr, nil, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pullFieldsFlag = flags.FieldsFlag(print.PullFields, []string{
|
||||||
|
"index", "title", "state", "author", "milestone", "updated", "labels",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdPullsList represents a sub command of issues to list pulls
|
||||||
|
var CmdPullsList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List pull requests of the repository",
|
||||||
|
Description: `List pull requests of the repository`,
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunPullsList,
|
||||||
|
Flags: append([]cli.Flag{pullFieldsFlag}, flags.PRListingFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunPullsList return list of pulls
|
||||||
|
func RunPullsList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
state := gitea.StateOpen
|
||||||
|
switch ctx.String("state") {
|
||||||
|
case "all":
|
||||||
|
state = gitea.StateAll
|
||||||
|
case "open":
|
||||||
|
state = gitea.StateOpen
|
||||||
|
case "closed":
|
||||||
|
state = gitea.StateClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
prs, _, err := ctx.Login.Client().ListRepoPullRequests(ctx.Owner, ctx.Repo, gitea.ListPullRequestsOptions{
|
||||||
|
State: state,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := pullFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.PullsList(prs, ctx.Output, fields)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsMerge merges a PR
|
||||||
|
var CmdPullsMerge = cli.Command{
|
||||||
|
Name: "merge",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Merge a pull request",
|
||||||
|
Description: "Merge a pull request",
|
||||||
|
ArgsUsage: "<pull index>",
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "style",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Usage: "Kind of merge to perform: merge, rebase, squash, rebase-merge",
|
||||||
|
Value: "merge",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "title",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Usage: "Merge commit title",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "message",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Merge commit message",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
if ctx.Args().Len() != 1 {
|
||||||
|
// If no PR index is provided, try interactive mode
|
||||||
|
return interact.MergePull(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.PullMerge(ctx.Login, ctx.Owner, ctx.Repo, idx, gitea.MergePullRequestOption{
|
||||||
|
Style: gitea.MergeStyle(ctx.String("style")),
|
||||||
|
Title: ctx.String("title"),
|
||||||
|
Message: ctx.String("message"),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/task"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsReject requests changes to a PR
|
||||||
|
var CmdPullsReject = cli.Command{
|
||||||
|
Name: "reject",
|
||||||
|
Usage: "Request changes to a pull request",
|
||||||
|
Description: "Request changes to a pull request",
|
||||||
|
ArgsUsage: "<pull index> <reason>",
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
if ctx.Args().Len() < 2 {
|
||||||
|
return fmt.Errorf("Must specify a PR index and comment")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
comment := strings.Join(ctx.Args().Tail(), " ")
|
||||||
|
|
||||||
|
return task.CreatePullReview(ctx, idx, gitea.ReviewStateRequestChanges, comment, nil)
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsReopen reopens a given closed pull request
|
||||||
|
var CmdPullsReopen = cli.Command{
|
||||||
|
Name: "reopen",
|
||||||
|
Aliases: []string{"open"},
|
||||||
|
Usage: "Change state of one or more pull requests to 'open'",
|
||||||
|
Description: `Change state of one or more pull requests to 'open'`,
|
||||||
|
ArgsUsage: "<pull index> [<pull index>...]",
|
||||||
|
Action: func(ctx *cli.Context) error {
|
||||||
|
var s = gitea.StateOpen
|
||||||
|
return editPullState(ctx, gitea.EditPullRequestOption{State: &s})
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package pulls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdPullsReview starts an interactive review session
|
||||||
|
var CmdPullsReview = cli.Command{
|
||||||
|
Name: "review",
|
||||||
|
Usage: "Interactively review a pull request",
|
||||||
|
Description: "Interactively review a pull request",
|
||||||
|
ArgsUsage: "<pull index>",
|
||||||
|
Action: func(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
if ctx.Args().Len() != 1 {
|
||||||
|
return fmt.Errorf("Must specify a PR index")
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := utils.ArgToIndex(ctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return interact.ReviewPull(ctx, idx)
|
||||||
|
},
|
||||||
|
Flags: flags.AllDefaultFlags,
|
||||||
|
}
|
148
cmd/releases.go
148
cmd/releases.go
|
@ -1,143 +1,31 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"code.gitea.io/tea/cmd/flags"
|
||||||
"os"
|
"code.gitea.io/tea/cmd/releases"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdReleases represents to login a gitea server.
|
// CmdReleases represents to login a gitea server.
|
||||||
|
// ToDo: ReleaseDetails
|
||||||
var CmdReleases = cli.Command{
|
var CmdReleases = cli.Command{
|
||||||
Name: "releases",
|
Name: "releases",
|
||||||
Usage: "Create releases",
|
Aliases: []string{"release", "r"},
|
||||||
Description: `Create releases`,
|
Category: catEntities,
|
||||||
Action: runReleases,
|
Usage: "Manage releases",
|
||||||
Subcommands: []cli.Command{
|
Description: "Manage releases",
|
||||||
CmdReleaseCreate,
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: releases.RunReleasesList,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&releases.CmdReleaseList,
|
||||||
|
&releases.CmdReleaseCreate,
|
||||||
|
&releases.CmdReleaseDelete,
|
||||||
|
&releases.CmdReleaseEdit,
|
||||||
|
&CmdReleaseAttachments,
|
||||||
},
|
},
|
||||||
Flags: AllDefaultFlags,
|
Flags: flags.AllDefaultFlags,
|
||||||
}
|
|
||||||
|
|
||||||
func runReleases(ctx *cli.Context) error {
|
|
||||||
login, owner, repo := initCommand()
|
|
||||||
|
|
||||||
releases, err := login.Client().ListReleases(owner, repo)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
headers := []string{
|
|
||||||
"Tag-Name",
|
|
||||||
"Title",
|
|
||||||
"Published At",
|
|
||||||
"Tar URL",
|
|
||||||
}
|
|
||||||
|
|
||||||
var values [][]string
|
|
||||||
|
|
||||||
if len(releases) == 0 {
|
|
||||||
Output(outputValue, headers, values)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, release := range releases {
|
|
||||||
values = append(
|
|
||||||
values,
|
|
||||||
[]string{
|
|
||||||
release.TagName,
|
|
||||||
release.Title,
|
|
||||||
release.PublishedAt.Format("2006-01-02 15:04:05"),
|
|
||||||
release.TarURL,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Output(outputValue, headers, values)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdReleaseCreate represents a sub command of Release to create release.
|
|
||||||
var CmdReleaseCreate = cli.Command{
|
|
||||||
Name: "create",
|
|
||||||
Usage: "Create a release",
|
|
||||||
Description: `Create a release`,
|
|
||||||
Action: runReleaseCreate,
|
|
||||||
Flags: append([]cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "tag",
|
|
||||||
Usage: "Tag name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "target",
|
|
||||||
Usage: "Target refs, branch name or commit id",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "title, t",
|
|
||||||
Usage: "Release title",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "note, n",
|
|
||||||
Usage: "Release notes",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "draft, d",
|
|
||||||
Usage: "Is a draft",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "prerelease, p",
|
|
||||||
Usage: "Is a pre-release",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "asset, a",
|
|
||||||
Usage: "List of files to attach",
|
|
||||||
},
|
|
||||||
}, LoginRepoFlags...),
|
|
||||||
}
|
|
||||||
|
|
||||||
func runReleaseCreate(ctx *cli.Context) error {
|
|
||||||
login, owner, repo := initCommand()
|
|
||||||
|
|
||||||
release, err := login.Client().CreateRelease(owner, repo, gitea.CreateReleaseOption{
|
|
||||||
TagName: ctx.String("tag"),
|
|
||||||
Target: ctx.String("target"),
|
|
||||||
Title: ctx.String("title"),
|
|
||||||
Note: ctx.String("note"),
|
|
||||||
IsDraft: ctx.Bool("draft"),
|
|
||||||
IsPrerelease: ctx.Bool("prerelease"),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "409 Conflict" {
|
|
||||||
log.Fatal("error: There already is a release for this tag")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, asset := range ctx.StringSlice("asset") {
|
|
||||||
var file *os.File
|
|
||||||
|
|
||||||
if file, err = os.Open(asset); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
filePath := filepath.Base(asset)
|
|
||||||
|
|
||||||
if _, err = login.Client().CreateReleaseAttachment(owner, repo, release.ID, file, filePath); err != nil {
|
|
||||||
file.Close()
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package releases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdReleaseCreate represents a sub command of Release to create release
|
||||||
|
var CmdReleaseCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create a release",
|
||||||
|
Description: `Create a release for a new or existing git tag`,
|
||||||
|
ArgsUsage: "[<tag>]",
|
||||||
|
Action: runReleaseCreate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "tag",
|
||||||
|
Usage: "Tag name. If the tag does not exist yet, it will be created by Gitea",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "target",
|
||||||
|
Usage: "Target branch name or commit hash. Defaults to the default branch of the repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "title",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Usage: "Release title",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "note",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Usage: "Release notes",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "draft",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Usage: "Is a draft",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "prerelease",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "Is a pre-release",
|
||||||
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "asset",
|
||||||
|
Aliases: []string{"a"},
|
||||||
|
Usage: "Path to file attachment. Can be specified multiple times",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runReleaseCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
tag := ctx.String("tag")
|
||||||
|
if cmd.Args().Present() {
|
||||||
|
if len(tag) != 0 {
|
||||||
|
return fmt.Errorf("ambiguous arguments: provide tagname via --tag or argument, but not both")
|
||||||
|
}
|
||||||
|
tag = cmd.Args().First()
|
||||||
|
}
|
||||||
|
|
||||||
|
release, resp, err := ctx.Login.Client().CreateRelease(ctx.Owner, ctx.Repo, gitea.CreateReleaseOption{
|
||||||
|
TagName: tag,
|
||||||
|
Target: ctx.String("target"),
|
||||||
|
Title: ctx.String("title"),
|
||||||
|
Note: ctx.String("note"),
|
||||||
|
IsDraft: ctx.Bool("draft"),
|
||||||
|
IsPrerelease: ctx.Bool("prerelease"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if resp != nil && resp.StatusCode == http.StatusConflict {
|
||||||
|
return fmt.Errorf("There already is a release for this tag")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, asset := range ctx.StringSlice("asset") {
|
||||||
|
var file *os.File
|
||||||
|
|
||||||
|
if file, err = os.Open(asset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath := filepath.Base(asset)
|
||||||
|
|
||||||
|
if _, _, err = ctx.Login.Client().CreateReleaseAttachment(ctx.Owner, ctx.Repo, release.ID, file, filePath); err != nil {
|
||||||
|
file.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package releases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdReleaseDelete represents a sub command of Release to delete a release
|
||||||
|
var CmdReleaseDelete = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "Delete one or more releases",
|
||||||
|
Description: `Delete one or more releases`,
|
||||||
|
ArgsUsage: "<release tag> [<release tag>...]",
|
||||||
|
Action: runReleaseDelete,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "confirm",
|
||||||
|
Aliases: []string{"y"},
|
||||||
|
Usage: "Confirm deletion (required)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "delete-tag",
|
||||||
|
Usage: "Also delete the git tag for this release",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runReleaseDelete(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
if !ctx.Args().Present() {
|
||||||
|
fmt.Println("Release tag needed to edit")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ctx.Bool("confirm") {
|
||||||
|
fmt.Println("Are you sure? Please confirm with -y or --confirm.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range ctx.Args().Slice() {
|
||||||
|
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = client.DeleteRelease(ctx.Owner, ctx.Repo, release.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Bool("delete-tag") {
|
||||||
|
_, err = client.DeleteTag(ctx.Owner, ctx.Repo, tag)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package releases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdReleaseEdit represents a sub command of Release to edit releases
|
||||||
|
var CmdReleaseEdit = cli.Command{
|
||||||
|
Name: "edit",
|
||||||
|
Aliases: []string{"e"},
|
||||||
|
Usage: "Edit one or more releases",
|
||||||
|
Description: `Edit one or more releases`,
|
||||||
|
ArgsUsage: "<release tag> [<release tag>...]",
|
||||||
|
Action: runReleaseEdit,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "tag",
|
||||||
|
Usage: "Change Tag",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "target",
|
||||||
|
Usage: "Change Target",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "title",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Usage: "Change Title",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "note",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Usage: "Change Notes",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "draft",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Usage: "Mark as Draft [True/false]",
|
||||||
|
DefaultText: "true",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "prerelease",
|
||||||
|
Aliases: []string{"p"},
|
||||||
|
Usage: "Mark as Pre-Release [True/false]",
|
||||||
|
DefaultText: "true",
|
||||||
|
},
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runReleaseEdit(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
var isDraft, isPre *bool
|
||||||
|
if ctx.IsSet("draft") {
|
||||||
|
isDraft = gitea.OptionalBool(strings.ToLower(ctx.String("draft"))[:1] == "t")
|
||||||
|
}
|
||||||
|
if ctx.IsSet("prerelease") {
|
||||||
|
isPre = gitea.OptionalBool(strings.ToLower(ctx.String("prerelease"))[:1] == "t")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ctx.Args().Present() {
|
||||||
|
fmt.Println("Release tag needed to edit")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range ctx.Args().Slice() {
|
||||||
|
release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = client.EditRelease(ctx.Owner, ctx.Repo, release.ID, gitea.EditReleaseOption{
|
||||||
|
TagName: ctx.String("tag"),
|
||||||
|
Target: ctx.String("target"),
|
||||||
|
Title: ctx.String("title"),
|
||||||
|
Note: ctx.String("note"),
|
||||||
|
IsDraft: isDraft,
|
||||||
|
IsPrerelease: isPre,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package releases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdReleaseList represents a sub command of Release to list releases
|
||||||
|
var CmdReleaseList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List Releases",
|
||||||
|
Description: "List Releases",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: RunReleasesList,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.AllDefaultFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunReleasesList list releases
|
||||||
|
func RunReleasesList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
|
||||||
|
releases, _, err := ctx.Login.Client().ListReleases(ctx.Owner, ctx.Repo, gitea.ListReleasesOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.ReleasesList(releases, ctx.Output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getReleaseByTag(owner, repo, tag string, client *gitea.Client) (*gitea.Release, error) {
|
||||||
|
rl, _, err := client.ListReleases(owner, repo, gitea.ListReleasesOptions{
|
||||||
|
ListOptions: gitea.ListOptions{Page: -1},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rl) == 0 {
|
||||||
|
return nil, fmt.Errorf("Repo does not have any release")
|
||||||
|
}
|
||||||
|
for _, r := range rl {
|
||||||
|
if r.TagName == tag {
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Release tag does not exist")
|
||||||
|
}
|
147
cmd/repos.go
147
cmd/repos.go
|
@ -1,132 +1,59 @@
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// SPDX-License-Identifier: MIT
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"code.gitea.io/tea/cmd/repos"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdRepos represents to login a gitea server.
|
// CmdRepos represents to login a gitea server.
|
||||||
var CmdRepos = cli.Command{
|
var CmdRepos = cli.Command{
|
||||||
Name: "repos",
|
Name: "repos",
|
||||||
Usage: "Operate with repositories",
|
Aliases: []string{"repo"},
|
||||||
Description: `Operate with repositories`,
|
Category: catEntities,
|
||||||
Action: runReposList,
|
Usage: "Show repository details",
|
||||||
Subcommands: []cli.Command{
|
Description: "Show repository details",
|
||||||
CmdReposList,
|
ArgsUsage: "[<repo owner>/<repo name>]",
|
||||||
|
Action: runRepos,
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
&repos.CmdReposList,
|
||||||
|
&repos.CmdReposSearch,
|
||||||
|
&repos.CmdRepoCreate,
|
||||||
|
&repos.CmdRepoCreateFromTemplate,
|
||||||
|
&repos.CmdRepoFork,
|
||||||
|
&repos.CmdRepoMigrate,
|
||||||
|
&repos.CmdRepoRm,
|
||||||
},
|
},
|
||||||
Flags: LoginOutputFlags,
|
Flags: repos.CmdReposListFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmdReposList represents a sub command of issues to list issues
|
func runRepos(ctx *cli.Context) error {
|
||||||
var CmdReposList = cli.Command{
|
if ctx.Args().Len() == 1 {
|
||||||
Name: "ls",
|
return runRepoDetail(ctx, ctx.Args().First())
|
||||||
Usage: "List available repositories",
|
}
|
||||||
Description: `List available repositories`,
|
return repos.RunReposList(ctx)
|
||||||
Action: runReposList,
|
|
||||||
Flags: append([]cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "mode",
|
|
||||||
Usage: "Filter listed repositories based on mode, optional - fork, mirror, source",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "org",
|
|
||||||
Usage: "Filter listed repositories based on organization, optional",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "user",
|
|
||||||
Usage: "Filter listed repositories absed on user, optional",
|
|
||||||
},
|
|
||||||
}, LoginOutputFlags...),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runReposList list repositories
|
func runRepoDetail(cmd *cli.Context, path string) error {
|
||||||
func runReposList(ctx *cli.Context) error {
|
ctx := context.InitCommand(cmd)
|
||||||
login := initCommandLoginOnly()
|
client := ctx.Login.Client()
|
||||||
|
repoOwner, repoName := utils.GetOwnerAndRepo(path, ctx.Owner)
|
||||||
mode := ctx.String("mode")
|
repo, _, err := client.GetRepo(repoOwner, repoName)
|
||||||
org := ctx.String("org")
|
|
||||||
user := ctx.String("user")
|
|
||||||
|
|
||||||
var rps []*gitea.Repository
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if org != "" {
|
|
||||||
rps, err = login.Client().ListOrgRepos(org)
|
|
||||||
} else if user != "" {
|
|
||||||
rps, err = login.Client().ListUserRepos(user)
|
|
||||||
} else {
|
|
||||||
rps, err = login.Client().ListMyRepos()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return err
|
||||||
|
}
|
||||||
|
topics, _, err := client.ListRepoTopics(repoOwner, repoName, gitea.ListRepoTopicsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var repos []*gitea.Repository
|
print.RepoDetails(repo, topics)
|
||||||
if mode == "" {
|
|
||||||
repos = rps
|
|
||||||
} else if mode == "fork" {
|
|
||||||
for _, rp := range rps {
|
|
||||||
if rp.Fork == true {
|
|
||||||
repos = append(repos, rp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if mode == "mirror" {
|
|
||||||
for _, rp := range rps {
|
|
||||||
if rp.Mirror == true {
|
|
||||||
repos = append(repos, rp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if mode == "source" {
|
|
||||||
for _, rp := range rps {
|
|
||||||
if rp.Mirror != true && rp.Fork != true {
|
|
||||||
repos = append(repos, rp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Fatal("Unknown mode: ", mode, "\nUse one of the following:\n- fork\n- mirror\n- source\n")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rps) == 0 {
|
|
||||||
log.Fatal("No repositories found", rps)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
headers := []string{
|
|
||||||
"Name",
|
|
||||||
"Type",
|
|
||||||
"SSH",
|
|
||||||
"Owner",
|
|
||||||
}
|
|
||||||
var values [][]string
|
|
||||||
|
|
||||||
for _, rp := range repos {
|
|
||||||
var mode = "source"
|
|
||||||
if rp.Fork {
|
|
||||||
mode = "fork"
|
|
||||||
}
|
|
||||||
if rp.Mirror {
|
|
||||||
mode = "mirror"
|
|
||||||
}
|
|
||||||
|
|
||||||
values = append(
|
|
||||||
values,
|
|
||||||
[]string{
|
|
||||||
rp.FullName,
|
|
||||||
mode,
|
|
||||||
rp.SSHURL,
|
|
||||||
rp.Owner.UserName,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Output(outputValue, headers, values)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoCreate represents a sub command of repos to create one
|
||||||
|
var CmdRepoCreate = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "Create a repository",
|
||||||
|
Description: "Create a repository",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runRepoCreate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{""},
|
||||||
|
Required: true,
|
||||||
|
Usage: "name of new repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner",
|
||||||
|
Aliases: []string{"O"},
|
||||||
|
Required: false,
|
||||||
|
Usage: "name of repo owner",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "private",
|
||||||
|
Required: false,
|
||||||
|
Value: false,
|
||||||
|
Usage: "make repo private",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Aliases: []string{"desc"},
|
||||||
|
Required: false,
|
||||||
|
Usage: "add description to repo",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "init",
|
||||||
|
Required: false,
|
||||||
|
Value: false,
|
||||||
|
Usage: "initialize repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "labels",
|
||||||
|
Required: false,
|
||||||
|
Usage: "name of label set to add",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "gitignores",
|
||||||
|
Aliases: []string{"git"},
|
||||||
|
Required: false,
|
||||||
|
Usage: "list of gitignore templates (need --init)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "license",
|
||||||
|
Required: false,
|
||||||
|
Usage: "add license (need --init)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "readme",
|
||||||
|
Required: false,
|
||||||
|
Usage: "use readme template (need --init)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "branch",
|
||||||
|
Required: false,
|
||||||
|
Usage: "use custom default branch (need --init)",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "template",
|
||||||
|
Usage: "make repo a template repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "trustmodel",
|
||||||
|
Usage: "select trust model (committer,collaborator,collaborator+committer)",
|
||||||
|
},
|
||||||
|
}, flags.LoginOutputFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoCreate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
var (
|
||||||
|
repo *gitea.Repository
|
||||||
|
err error
|
||||||
|
trustmodel gitea.TrustModel
|
||||||
|
)
|
||||||
|
|
||||||
|
if ctx.IsSet("trustmodel") {
|
||||||
|
switch ctx.String("trustmodel") {
|
||||||
|
case "committer":
|
||||||
|
trustmodel = gitea.TrustModelCommitter
|
||||||
|
case "collaborator":
|
||||||
|
trustmodel = gitea.TrustModelCollaborator
|
||||||
|
case "collaborator+committer":
|
||||||
|
trustmodel = gitea.TrustModelCollaboratorCommitter
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown trustmodel type '%s'", ctx.String("trustmodel"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := gitea.CreateRepoOption{
|
||||||
|
Name: ctx.String("name"),
|
||||||
|
Description: ctx.String("description"),
|
||||||
|
Private: ctx.Bool("private"),
|
||||||
|
AutoInit: ctx.Bool("init"),
|
||||||
|
IssueLabels: ctx.String("labels"),
|
||||||
|
Gitignores: ctx.String("gitignores"),
|
||||||
|
License: ctx.String("license"),
|
||||||
|
Readme: ctx.String("readme"),
|
||||||
|
DefaultBranch: ctx.String("branch"),
|
||||||
|
Template: ctx.Bool("template"),
|
||||||
|
TrustModel: trustmodel,
|
||||||
|
}
|
||||||
|
if len(ctx.String("owner")) != 0 {
|
||||||
|
repo, _, err = client.CreateOrgRepo(ctx.String("owner"), opts)
|
||||||
|
} else {
|
||||||
|
repo, _, err = client.CreateRepo(opts)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.RepoDetails(repo, topics)
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", repo.HTMLURL)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoCreateFromTemplate represents a sub command of repos to generate one from a template repo
|
||||||
|
var CmdRepoCreateFromTemplate = cli.Command{
|
||||||
|
Name: "create-from-template",
|
||||||
|
Aliases: []string{"ct"},
|
||||||
|
Usage: "Create a repository based on an existing template",
|
||||||
|
Description: "Create a repository based on an existing template",
|
||||||
|
Action: runRepoCreateFromTemplate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "template",
|
||||||
|
Aliases: []string{"t"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "source template to copy from",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{"n"},
|
||||||
|
Required: true,
|
||||||
|
Usage: "name of new repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner",
|
||||||
|
Aliases: []string{"O"},
|
||||||
|
Usage: "name of repo owner",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "private",
|
||||||
|
Usage: "make new repo private",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "description",
|
||||||
|
Aliases: []string{"desc"},
|
||||||
|
Usage: "add custom description to repo",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "content",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy git content from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "githooks",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy git hooks from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "avatar",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy repo avatar from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "labels",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy repo labels from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "topics",
|
||||||
|
Value: true,
|
||||||
|
Usage: "copy topics from template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "webhooks",
|
||||||
|
Usage: "copy webhooks from template",
|
||||||
|
},
|
||||||
|
}, flags.LoginOutputFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoCreateFromTemplate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
templateOwner, templateRepo := utils.GetOwnerAndRepo(ctx.String("template"), ctx.Login.User)
|
||||||
|
owner := ctx.Login.User
|
||||||
|
if ctx.IsSet("owner") {
|
||||||
|
owner = ctx.String("owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := gitea.CreateRepoFromTemplateOption{
|
||||||
|
Name: ctx.String("name"),
|
||||||
|
Owner: owner,
|
||||||
|
Description: ctx.String("description"),
|
||||||
|
Private: ctx.Bool("private"),
|
||||||
|
GitContent: ctx.Bool("content"),
|
||||||
|
GitHooks: ctx.Bool("githooks"),
|
||||||
|
Avatar: ctx.Bool("avatar"),
|
||||||
|
Labels: ctx.Bool("labels"),
|
||||||
|
Topics: ctx.Bool("topics"),
|
||||||
|
Webhooks: ctx.Bool("webhooks"),
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, _, err := client.CreateRepoFromTemplate(templateOwner, templateRepo, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.RepoDetails(repo, topics)
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", repo.HTMLURL)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
|
||||||
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoRm represents a sub command of repos to delete an existing repo
|
||||||
|
var CmdRepoRm = cli.Command{
|
||||||
|
Name: "delete",
|
||||||
|
Aliases: []string{"rm"},
|
||||||
|
Usage: "Delete an existing repository",
|
||||||
|
Description: "Removes a repository from Create a repository from an existing repo",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runRepoDelete,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Aliases: []string{""},
|
||||||
|
Required: true,
|
||||||
|
Usage: "name of the repo",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner",
|
||||||
|
Aliases: []string{"O"},
|
||||||
|
Required: false,
|
||||||
|
Usage: "owner of the repo",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "force",
|
||||||
|
Aliases: []string{"f"},
|
||||||
|
Required: false,
|
||||||
|
Value: false,
|
||||||
|
Usage: "Force the deletion and don't ask for confirmation",
|
||||||
|
},
|
||||||
|
}, flags.LoginOutputFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoDelete(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
var owner string
|
||||||
|
if ctx.IsSet("owner") {
|
||||||
|
owner = ctx.String("owner")
|
||||||
|
|
||||||
|
} else {
|
||||||
|
owner = ctx.Login.User
|
||||||
|
}
|
||||||
|
|
||||||
|
repoName := ctx.String("name")
|
||||||
|
|
||||||
|
repoSlug := fmt.Sprintf("%s/%s", owner, repoName)
|
||||||
|
|
||||||
|
if !ctx.Bool("force") {
|
||||||
|
var enteredRepoSlug string
|
||||||
|
promptRepoName := &survey.Input{
|
||||||
|
Message: fmt.Sprintf("Confirm the deletion of the repository '%s' by typing its name: ", repoSlug),
|
||||||
|
}
|
||||||
|
if err := survey.AskOne(promptRepoName, &enteredRepoSlug, survey.WithValidator(survey.Required)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if enteredRepoSlug != repoSlug {
|
||||||
|
return fmt.Errorf("Entered wrong repository name '%s', expected '%s'", enteredRepoSlug, repoSlug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := client.DeleteRepo(owner, repoName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Successfully deleted %s/%s\n", owner, repoName)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var typeFilterFlag = cli.StringFlag{
|
||||||
|
Name: "type",
|
||||||
|
Aliases: []string{"T"},
|
||||||
|
Required: false,
|
||||||
|
Usage: "Filter by type: fork, mirror, source",
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTypeFilter(ctx *cli.Context) (filter gitea.RepoType, err error) {
|
||||||
|
t := ctx.String("type")
|
||||||
|
filter = gitea.RepoTypeNone
|
||||||
|
switch t {
|
||||||
|
case "":
|
||||||
|
filter = gitea.RepoTypeNone
|
||||||
|
case "fork":
|
||||||
|
filter = gitea.RepoTypeFork
|
||||||
|
case "mirror":
|
||||||
|
filter = gitea.RepoTypeMirror
|
||||||
|
case "source":
|
||||||
|
filter = gitea.RepoTypeSource
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("invalid repo type '%s'. valid: fork, mirror, source", t)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoFork represents a sub command of repos to fork an existing repo
|
||||||
|
var CmdRepoFork = cli.Command{
|
||||||
|
Name: "fork",
|
||||||
|
Aliases: []string{"f"},
|
||||||
|
Usage: "Fork an existing repository",
|
||||||
|
Description: "Create a repository from an existing repo",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runRepoFork,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner",
|
||||||
|
Aliases: []string{"O"},
|
||||||
|
Usage: "name of fork's owner, defaults to current user",
|
||||||
|
},
|
||||||
|
}, flags.LoginRepoFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoFork(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
opts := gitea.CreateForkOption{}
|
||||||
|
if ctx.IsSet("owner") {
|
||||||
|
owner := ctx.String("owner")
|
||||||
|
opts.Organization = &owner
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, _, err := client.CreateFork(ctx.Owner, ctx.Repo, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.RepoDetails(repo, topics)
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", repo.HTMLURL)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var repoFieldsFlag = flags.FieldsFlag(print.RepoFields, []string{
|
||||||
|
"owner", "name", "type", "ssh",
|
||||||
|
})
|
||||||
|
|
||||||
|
// CmdReposListFlags contains all flags needed for repo listing
|
||||||
|
var CmdReposListFlags = append([]cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "watched",
|
||||||
|
Aliases: []string{"w"},
|
||||||
|
Required: false,
|
||||||
|
Usage: "List your watched repos instead",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "starred",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Required: false,
|
||||||
|
Usage: "List your starred repos instead",
|
||||||
|
},
|
||||||
|
repoFieldsFlag,
|
||||||
|
&typeFilterFlag,
|
||||||
|
&flags.PaginationPageFlag,
|
||||||
|
&flags.PaginationLimitFlag,
|
||||||
|
}, flags.LoginOutputFlags...)
|
||||||
|
|
||||||
|
// CmdReposList represents a sub command of repos to list them
|
||||||
|
var CmdReposList = cli.Command{
|
||||||
|
Name: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Usage: "List repositories you have access to",
|
||||||
|
Description: "List repositories you have access to",
|
||||||
|
Action: RunReposList,
|
||||||
|
Flags: CmdReposListFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunReposList list repositories
|
||||||
|
func RunReposList(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
|
||||||
|
typeFilter, err := getTypeFilter(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rps []*gitea.Repository
|
||||||
|
if ctx.Bool("starred") {
|
||||||
|
user, _, err := client.GetMyUserInfo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rps, _, err = client.SearchRepos(gitea.SearchRepoOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
StarredByUserID: user.ID,
|
||||||
|
})
|
||||||
|
} else if ctx.Bool("watched") {
|
||||||
|
rps, _, err = client.GetMyWatchedRepos() // TODO: this does not expose pagination..
|
||||||
|
} else {
|
||||||
|
rps, _, err = client.ListMyRepos(gitea.ListReposOptions{
|
||||||
|
ListOptions: ctx.GetListOptions(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reposFiltered := rps
|
||||||
|
if typeFilter != gitea.RepoTypeNone {
|
||||||
|
reposFiltered = filterReposByType(rps, typeFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, err := repoFieldsFlag.GetValues(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.ReposList(reposFiltered, ctx.Output, fields)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterReposByType(repos []*gitea.Repository, t gitea.RepoType) []*gitea.Repository {
|
||||||
|
var filtered []*gitea.Repository
|
||||||
|
for _, r := range repos {
|
||||||
|
switch t {
|
||||||
|
case gitea.RepoTypeFork:
|
||||||
|
if !r.Fork {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case gitea.RepoTypeMirror:
|
||||||
|
if !r.Mirror {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case gitea.RepoTypeSource:
|
||||||
|
if r.Fork || r.Mirror {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered = append(filtered, r)
|
||||||
|
}
|
||||||
|
return filtered
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package repos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmdRepoMigrate represents a sub command of repos to migrate one
|
||||||
|
var CmdRepoMigrate = cli.Command{
|
||||||
|
Name: "migrate",
|
||||||
|
Aliases: []string{"m"},
|
||||||
|
Usage: "Migrate a repository",
|
||||||
|
Description: "Migrate a repository and or mirror it.",
|
||||||
|
ArgsUsage: " ", // command does not accept arguments
|
||||||
|
Action: runRepoMigrate,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Usage: "Name of the repository",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "owner",
|
||||||
|
Usage: "Owner of the repository",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "clone-url",
|
||||||
|
Usage: "Clone URL of the repository",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "service",
|
||||||
|
Usage: string("Service to migrate from. Supported services are: " + gitea.GitServicePlain +
|
||||||
|
", " + gitea.GitServiceGitea + ", " + gitea.GitServiceGitlab + ", " + gitea.GitServiceGogs),
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "mirror",
|
||||||
|
Usage: "Mirror the repository",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "private",
|
||||||
|
Usage: "Make the repository private",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "template",
|
||||||
|
Usage: "Make the repository a template",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "wiki",
|
||||||
|
Usage: "Copy the wiki",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "issues",
|
||||||
|
Usage: "Copy the issues",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "pull-requests",
|
||||||
|
Usage: "Copy the pull requests",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "releases",
|
||||||
|
Usage: "Copy the releases",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "milestones",
|
||||||
|
Usage: "Copy the milestones",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "mirror-interval",
|
||||||
|
Usage: "Interval to mirror the repository.",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "lfs",
|
||||||
|
Usage: "Copy the LFS objects",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "lfs-endpoint",
|
||||||
|
Usage: "LFS endpoint to use",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "auth-user",
|
||||||
|
Usage: "Username to use for authentication.",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "auth-password",
|
||||||
|
Usage: "Password to use for authentication.",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "auth-token",
|
||||||
|
Usage: "Token to use for authentication.",
|
||||||
|
},
|
||||||
|
}, flags.LoginOutputFlags...),
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRepoMigrate(cmd *cli.Context) error {
|
||||||
|
ctx := context.InitCommand(cmd)
|
||||||
|
client := ctx.Login.Client()
|
||||||
|
var (
|
||||||
|
repo *gitea.Repository
|
||||||
|
err error
|
||||||
|
service gitea.GitServiceType
|
||||||
|
)
|
||||||
|
|
||||||
|
if ctx.IsSet("service") {
|
||||||
|
switch ctx.String("service") {
|
||||||
|
case "git":
|
||||||
|
service = gitea.GitServicePlain
|
||||||
|
case "gitea":
|
||||||
|
service = gitea.GitServiceGitea
|
||||||
|
case "gitlab":
|
||||||
|
service = gitea.GitServiceGitlab
|
||||||
|
case "gogs":
|
||||||
|
service = gitea.GitServiceGogs
|
||||||
|
case "github":
|
||||||
|
service = gitea.GitServiceGithub
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown git service type '%s'", ctx.String("service"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := gitea.MigrateRepoOption{
|
||||||
|
RepoName: ctx.String("name"),
|
||||||
|
RepoOwner: ctx.String("owner"),
|
||||||
|
CloneAddr: ctx.String("clone-url"),
|
||||||
|
Service: service,
|
||||||
|
AuthUsername: ctx.String("auth-user"),
|
||||||
|
AuthPassword: ctx.String("auth-password"),
|
||||||
|
AuthToken: ctx.String("auth-token"),
|
||||||
|
Mirror: ctx.Bool("mirror"),
|
||||||
|
Private: ctx.Bool("private"),
|
||||||
|
Description: ctx.String("description"),
|
||||||
|
Wiki: ctx.Bool("wiki"),
|
||||||
|
Milestones: ctx.Bool("milestones"),
|
||||||
|
Labels: ctx.Bool("labels"),
|
||||||
|
Issues: ctx.Bool("issues"),
|
||||||
|
PullRequests: ctx.Bool("pull-requests"),
|
||||||
|
Releases: ctx.Bool("releases"),
|
||||||
|
MirrorInterval: ctx.String("mirror-interval"),
|
||||||
|
LFS: ctx.Bool("lfs"),
|
||||||
|
LFSEndpoint: ctx.String("lfs-endpoint"),
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, _, err = client.MigrateRepo(opts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
print.RepoDetails(repo, topics)
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", repo.HTMLURL)
|
||||||
|
return nil
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue