pax_global_header00006660000000000000000000000064143732632050014517gustar00rootroot0000000000000052 comment=fff1af1029a1c63c078e058c9a5c4d3ac7054594 tea/000077500000000000000000000000001437326320500116345ustar00rootroot00000000000000tea/.changelog.yml000077500000000000000000000016261437326320500143740ustar00rootroot00000000000000# 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\/.+ tea/.dockerignore000066400000000000000000000000171437326320500143060ustar00rootroot00000000000000Dockerfile tea tea/.drone.yml000066400000000000000000000103401437326320500135420ustar00rootroot00000000000000--- kind: pipeline name: default platform: os: linux arch: amd64 steps: - name: vendor pull: always image: golang:1.18 environment: GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not commands: - make vendor # use vendor folder as cache - name: build pull: always image: golang:1.18 environment: GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not commands: - make clean - make vet - make lint - make fmt-check - make misspell-check - make build when: event: - push - tag - pull_request - name: unit-test image: golang:1.18 commands: - make unit-test-coverage settings: group: test environment: GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not when: branch: - main event: - push - pull_request - name: release-test image: golang:1.18 commands: - make test settings: group: test environment: GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not when: branch: - "release/*" event: - push - pull_request - name: tag-test pull: always image: golang:1.18 commands: - make test settings: group: test environment: GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not when: event: - tag - name: static image: golang:1.18 environment: GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not commands: - 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: woodpeckerci/plugin-s3:latest settings: acl: from_secret: aws_s3_acl region: from_secret: aws_s3_region bucket: from_secret: aws_s3_bucket endpoint: from_secret: aws_s3_endpoint path_style: from_secret: aws_s3_path_style source: "dist/release/*" 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: woodpeckerci/plugin-s3:latest settings: acl: from_secret: aws_s3_acl region: from_secret: aws_s3_region bucket: from_secret: aws_s3_bucket endpoint: from_secret: aws_s3_endpoint path_style: from_secret: aws_s3_path_style source: "dist/release/*" 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: woodpeckerci/plugin-s3:latest settings: acl: from_secret: aws_s3_acl region: from_secret: aws_s3_region bucket: from_secret: aws_s3_bucket endpoint: from_secret: aws_s3_endpoint path_style: from_secret: aws_s3_path_style source: "dist/release/*" strip_prefix: dist/release/ target: /tea/main environment: AWS_ACCESS_KEY_ID: from_secret: aws_access_key_id AWS_SECRET_ACCESS_KEY: from_secret: aws_secret_access_key when: branch: - main event: - push - name: gitea pull: always image: plugins/gitea-release:1 settings: files: - "dist/release/*" base_url: https://gitea.com api_key: from_secret: gitea_token when: event: - tag - name: discord pull: always image: appleboy/drone-discord:1.0.0 environment: DISCORD_WEBHOOK_ID: from_secret: discord_webhook_id DISCORD_WEBHOOK_TOKEN: from_secret: discord_webhook_token when: event: - push - tag - pull_request status: - changed - failure tea/.gitea/000077500000000000000000000000001437326320500130035ustar00rootroot00000000000000tea/.gitea/issue_template/000077500000000000000000000000001437326320500160265ustar00rootroot00000000000000tea/.gitea/issue_template/bug.md000066400000000000000000000016511437326320500171300ustar00rootroot00000000000000--- name: "Bug Report" about: "Use this template when reporting a bug, so you don't forget important information we'd ask for later." title: "Bug: " labels: - kind/bug --- ### describe your environment - tea version used (`tea -v`): - [ ] I also reproduced the issue [with the latest master build](https://dl.gitea.io/tea/master) - Gitea version used: - [ ] the issue only occurred after updating gitea recently - operating system: - I make use of... - [ ] non-standard default branch names (no `main`,`master`, or `trunk`) - [ ] .ssh/config or .gitconfig host aliases in my git remotes - [ ] ssh_agent or similar - [ ] non-standard ports for gitea and/or ssh - [ ] something else that's likely to interact badly with tea: ... Please provide the output of `git remote -v` (if the issue is related to tea not finding resources on Gitea): ``` ``` ### describe the issue (observed vs expected behaviour) tea/.gitignore000066400000000000000000000000711437326320500136220ustar00rootroot00000000000000tea /gitea-vet .idea/ .history/ dist/ .vscode/ vendor/ tea/.revive.toml000066400000000000000000000007301437326320500141070ustar00rootroot00000000000000ignoreGeneratedHeader = false severity = "warning" confidence = 0.8 errorCode = 1 warningCode = 1 [rule.blank-imports] [rule.context-as-argument] [rule.context-keys-type] [rule.dot-imports] [rule.error-return] [rule.error-strings] [rule.error-naming] [rule.exported] [rule.if-return] [rule.increment-decrement] [rule.var-naming] [rule.var-declaration] [rule.range] [rule.receiver-naming] [rule.time-naming] [rule.unexported-return] [rule.indent-error-flow] [rule.errorf] tea/CHANGELOG.md000066400000000000000000000217531437326320500134550ustar00rootroot00000000000000# 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 ` (#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 ` * 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) tea/CONTRIBUTING.md000066400000000000000000000235341437326320500140740ustar00rootroot00000000000000# Contribution Guidelines ## Table of Contents - [Contribution Guidelines](#contribution-guidelines) - [Introduction](#introduction) - [Bug reports](#bug-reports) - [Discuss your design](#discuss-your-design) - [Testing redux](#testing-redux) - [Vendoring](#vendoring) - [Code review](#code-review) - [Styleguide](#styleguide) - [Sign-off your work](#sign-off-your-work) - [Release Cycle](#release-cycle) - [Maintainers](#maintainers) - [Owners](#owners) - [Versions](#versions) - [Copyright](#copyright) ## Introduction This document explains how to contribute changes to TEA. Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io). For configuring IDE or code editor to develop Gitea see [IDE and code editor configuration](https://github.com/go-gitea/gitea/tree/master/contrib/ide) ## Bug reports Please search the issues on the issue tracker with a variety of keywords to ensure your bug is not already reported. If unique, [open an issue](https://gitea.com/gitea/tea/issues/new). Please write clear, concise instructions so we can reproduce the behavior— even if it seems obvious. The more detailed and specific you are, the faster we can fix the issue. Check out [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html). Please be kind, remember that TEA comes at no cost to you, and you're getting free help. ## Discuss your design The project welcomes submissions. If you want to change or add something, please let everyone know what you're working on—[file an issue](https://gitea.com/gitea/tea/issues/new)! Significant changes must go through the change proposal process before they can be accepted. To create a proposal, file an issue with your proposed changes documented, and make sure to note in the title of the issue that it is a proposal. This process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside the goals for the project and tools. It also checks that the design is sound before code is written; the code review tool is not the place for high-level discussions. ## Testing redux Before sending code out for review, run all the test by executing: `make test` Since TEA is an cli tool it should be obvious to test your feature locally first. ## Vendoring We keep a cached copy of dependencies within the `vendor/` directory, managing updates via [dep](https://github.com/golang/dep). Pull requests should only include `vendor/` updates if they are part of the same change, be it a bugfix or a feature addition. The `vendor/` update needs to be justified as part of the PR description, and must be verified by the reviewers and/or merger to always reference an existing upstream commit. You can find more information on how to get started with it on the [dep project website](https://golang.github.io/dep/docs/introduction.html). ## Code review Changes to TEA must be reviewed before they are accepted—no matter who makes the change, even if they are an owner or a maintainer. We use Gitea's pull request & review workflow to do that. Gitea ensure every PR is reviewed by at least 2 maintainers. 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; it has lots of useful tips for any project you may want to contribute. Some of the key points: * Make small pull requests. The smaller, the faster to review and the more likely it will be merged soon. * Don't make changes unrelated to your PR. Maybe there are typos on some comments, maybe refactoring would be welcome on a function... but if that is not related to your PR, please make *another* PR for that. * Split big pull requests into multiple small ones. An incremental change will be faster to review than a huge PR. ## Styleguide ### Commands - Subcommands should follow the following structure: ``` tea [] [] ``` for example: ``` tea issues list tea pulls create tea teams add user --team x --user y ``` - Commands should accept nouns as singular & plural by making use of the `Aliases` field. - The default action without a verb is `list`. - There is a standard set of verbs: `list/ls`, `create`, `edit`, `delete` - `ls` lists objects with filter options, and applies pagination where available. - `delete` should show info what is deleted and ask user again, if force flag`-y` is not set - Verbs that accept large numbers of flags provide an interactive mode when called without any arguments or flags. - Try to reuse as many flag definitions as possible, see `cmd/flags/flags.go`. - Always make sure that the help texts are properly set, and as concise as possible. ### Internal Module Structure - `cmd`: only contains command/flag options for `urfave/cli` - subcommands are in a subpackage named after its parent command - `modules/task`: contain func for doing something with gitea (e.g. create token by user/passwd) - `modules/print`: contain all functions that print to stdout - `modules/config`: config tea & login things - `modules/interact`: contain functions to interact with user by prompts - `modules/git`: do git related stuff (get info/push/pull/checkout/...) - `modules/utils`: helper functions used by other functions ### Code Style Use `make fmt`, check with `make lint`. For imports you should use the following format (_without_ the comments) ```go import ( // stdlib "encoding/json" "fmt" // local packages "code.gitea.io/gitea/models" "code.gitea.io/sdk/gitea" // external packages "github.com/foo/bar" "gopkg.io/baz.v1" ) ``` ## Sign-off your work The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: If you can certify [DCO](DCO), then you just add a line to every git commit message: ``` Signed-off-by: Joe Smith ``` Please use your real name; we really dislike pseudonyms or anonymous contributions. We are in the open-source world without secrets. If you set your `user.name` and `user.email` git configs, you can sign-off your commit automatically with `git commit -s`. ## Release Cycle Before we reach v1 there is no fixed release cycle. ## Maintainers To make sure every PR is checked, we have [team maintainers](MAINTAINERS). Every PR **MUST** be reviewed by at least two maintainers (or owners) before it can get merged. A maintainer should be a contributor of Gitea (or Gogs) and contributed at least 4 accepted PRs. A contributor should apply as a maintainer in the [Discord](https://discord.gg/NsatcWJ) #develop channel. The owners or the team maintainers may invite the contributor. A maintainer should spend some time on code reviews. If a maintainer has no time to do that, they should apply to leave the maintainers team and we will give them the honor of being a member of the [advisors team](https://github.com/orgs/go-gitea/teams/advisors). Of course, if an advisor has time to code review, we will gladly welcome them back to the maintainers team. If a maintainer is inactive for more than 3 months and forgets to leave the maintainers team, the owners may move him or her from the maintainers team to the advisors team. For security reasons, Maintainers should use 2FA for their accounts and if possible provide gpg signed commits. https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/ https://help.github.com/articles/signing-commits-with-gpg/ ## Owners This repo is part of the Gitea project and as such part of that project's governance. Since Gitea is a pure community organization without any company support, to keep the development healthy we will elect three owners every year. All contributors may vote to elect up to three candidates, one of which will be the main owner, and the other two the assistant owners. When the new owners have been elected, the old owners will give up ownership to the newly elected owners. If an owner is unable to do so, the other owners will assist in ceding ownership to the newly elected owners. For security reasons, Owners or any account with write access (like a bot) must use 2FA. https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/ After the election, the new owners should proactively agree with our [CONTRIBUTING](CONTRIBUTING.md) requirements in the [Discord](https://discord.gg/NsatcWJ) #general channel. Below are the words to speak: ``` I'm honored to having been elected an owner of Gitea, I agree with [CONTRIBUTING](CONTRIBUTING.md). I will spend part of my time on Gitea and lead the development of Gitea. ``` ## Versions 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 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, after bringing the bug fix also to the master branch. Since the `master` branch is a tip version, if you wish to use Gitea in production, please download the latest release tag version. All the branches will be protected via GitHub, all the PRs to every branch must be reviewed by two maintainers and must pass the automatic tests. ## Copyright Code that you contribute should use the standard copyright header: ``` // Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. ``` Files in the repository contain copyright from the year they are added to the year they are last changed. If the copyright author is changed, just paste the header below the old one. tea/DCO000066400000000000000000000026151437326320500121700ustar00rootroot00000000000000Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.tea/Dockerfile000066400000000000000000000012701437326320500136260ustar00rootroot00000000000000ARG GOVERSION="1.16.2" FROM golang:${GOVERSION}-alpine AS buildenv ARG GOOS="linux" COPY . $GOPATH/src/ WORKDIR $GOPATH/src RUN apk add --quiet --no-cache \ build-base \ make \ git && \ make clean build STATIC=true FROM scratch ARG VERSION="0.7.0" LABEL org.opencontainers.image.title="tea - CLI for Gitea - git with a cup of tea" LABEL org.opencontainers.image.description="A command line tool to interact with Gitea servers" LABEL org.opencontainers.image.version="${VERSION}" LABEL org.opencontainers.image.authors="Tamás Gérczei " LABEL org.opencontainers.image.vendor="The Gitea Authors" COPY --from=buildenv /go/src/tea / ENV HOME="/app" ENTRYPOINT ["/tea"] tea/FEATURE-COMPARISON.md000066400000000000000000000041371437326320500147260ustar00rootroot00000000000000# 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 tea/LICENSE000066400000000000000000000021111437326320500126340ustar00rootroot00000000000000Copyright (c) 2016 The Gitea Authors Copyright (c) 2015 The Gogs Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tea/Makefile000066400000000000000000000100461437326320500132750ustar00rootroot00000000000000DIST := dist export GO111MODULE=on export CGO_ENABLED=0 GO ?= go SHASUM ?= shasum -a 256 export PATH := $($(GO) env GOPATH)/bin:$(PATH) GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go") GOFMT ?= gofmt -s ifneq ($(DRONE_TAG),) VERSION ?= $(subst v,,$(DRONE_TAG)) TEA_VERSION ?= $(VERSION) else ifneq ($(DRONE_BRANCH),) VERSION ?= $(subst release/v,,$(DRONE_BRANCH)) else VERSION ?= master endif TEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') endif TEA_VERSION_TAG ?= $(shell sed 's/+/_/' <<< $(TEA_VERSION)) TAGS ?= SDK ?= $(shell $(GO) list -f '{{.Version}}' -m code.gitea.io/sdk/gitea) LDFLAGS := -X "main.Version=$(TEA_VERSION)" -X "main.Tags=$(TAGS)" -X "main.SDK=$(SDK)" -s -w # override to allow passing additional goflags via make CLI override GOFLAGS := $(GOFLAGS) -mod=vendor -tags '$(TAGS)' -ldflags '$(LDFLAGS)' PACKAGES ?= $(shell $(GO) list ./... | grep -v /vendor/) SOURCES ?= $(shell find . -name "*.go" -type f) # OS specific vars. ifeq ($(OS), Windows_NT) EXECUTABLE := tea.exe else EXECUTABLE := tea ifneq ($(shell uname -s), OpenBSD) override BUILDMODE := -buildmode=pie endif endif .PHONY: all all: build .PHONY: clean clean: $(GO) clean -mod=vendor -i ./... rm -rf $(EXECUTABLE) $(DIST) .PHONY: fmt fmt: $(GOFMT) -w $(GOFILES) .PHONY: vet vet: # Default vet $(GO) vet -mod=vendor $(PACKAGES) # Custom vet $(GO) build -mod=vendor code.gitea.io/gitea-vet $(GO) vet -vettool=gitea-vet $(PACKAGES) .PHONY: lint lint: install-lint-tools revive -config .revive.toml -exclude=./vendor/... ./... || exit 1 .PHONY: misspell-check misspell-check: install-lint-tools misspell -error -i unknwon,destory $(GOFILES) .PHONY: misspell misspell: install-lint-tools misspell -w -i unknwon $(GOFILES) .PHONY: fmt-check fmt-check: # get all go files and run go fmt on them @diff=$$($(GOFMT) -d $(GOFILES)); \ if [ -n "$$diff" ]; then \ echo "Please run 'make fmt' and commit the result:"; \ echo "$${diff}"; \ exit 1; \ fi; .PHONY: test test: $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' $(PACKAGES) .PHONY: unit-test-coverage unit-test-coverage: $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 .PHONY: vendor vendor: $(GO) mod tidy && $(GO) mod vendor .PHONY: check check: test .PHONY: install install: $(SOURCES) @echo "installing to $(GOPATH)/bin/$(EXECUTABLE)" $(GO) install -v $(BUILDMODE) $(GOFLAGS) .PHONY: build build: $(EXECUTABLE) $(EXECUTABLE): $(SOURCES) $(GO) build $(BUILDMODE) $(GOFLAGS) -o $@ .PHONY: build-image build-image: docker build --build-arg VERSION=$(TEA_VERSION) -t gitea/tea:$(TEA_VERSION_TAG) . .PHONY: release release: release-dirs install-release-tools release-os release-compress release-check .PHONY: release-dirs release-dirs: mkdir -p $(DIST)/binaries $(DIST)/release .PHONY: release-os release-os: CGO_ENABLED=0 gox -verbose -cgo=false $(GOFLAGS) -osarch='!darwin/386 !darwin/arm' -os="windows linux darwin" -arch="386 amd64 arm arm64" -output="$(DIST)/release/tea-$(VERSION)-{{.OS}}-{{.Arch}}" .PHONY: release-compress release-compress: install-release-tools cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done; .PHONY: release-check release-check: install-release-tools cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done; ### tools install-release-tools: @hash gox > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ $(GO) install github.com/mitchellh/gox@latest; \ fi @hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ $(GO) install github.com/ulikunitz/xz/cmd/gxz@latest; \ fi install-lint-tools: @hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ $(GO) install github.com/mgechev/revive@latest; \ fi @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ $(GO) install github.com/client9/misspell/cmd/misspell@latest; \ fi tea/README.md000066400000000000000000000133671437326320500131250ustar00rootroot00000000000000# *T E A* [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Release](https://raster.shields.io/badge/dynamic/json.svg?label=release&url=https://gitea.com/api/v1/repos/gitea/tea/releases&query=$[0].tag_name)](https://gitea.com/gitea/tea/releases) [![Build Status](https://drone.gitea.com/api/badges/gitea/tea/status.svg)](https://drone.gitea.com/gitea/tea) [![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea) [![Go Report Card](https://goreportcard.com/badge/code.gitea.io/tea)](https://goreportcard.com/report/code.gitea.io/tea) [![GoDoc](https://godoc.org/code.gitea.io/tea?status.svg)](https://godoc.org/code.gitea.io/tea) ### 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 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://gitea.io. ``` - [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 There are different ways to get `tea`: 1. Install via your system package manager: - macOS via `brew` (gitea-maintained): ```sh brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea brew install tea ``` - arch linux ([gitea-tea-git](https://aur.archlinux.org/packages/gitea-tea-git), thirdparty) - alpine linux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge), thirdparty) 2. Use the prebuilt binaries from [dl.gitea.io](https://dl.gitea.io/tea/) 3. Install from source: [see *Compilation*](#compilation) 4. Docker (thirdparty): [tgerczei/tea](https://hub.docker.com/r/tgerczei/tea) ## Compilation Make sure you have a current go version installed (1.13 or newer). - To compile the source yourself with the recommended flags & tags: ```sh git clone https://gitea.com/gitea/tea.git # or: tea clone gitea.com/gitea/tea ;) cd tea make ``` Note that GNU Make (gmake on OpenBSD) is required. - For a quick installation without `git` & `make`: ```sh go install code.gitea.io/tea@latest ``` ## Contributing Fork -> Patch -> Push -> Pull Request - `make test` run testsuite - `make vet` run checks (check the order of imports; preventing failure on CI pipeline beforehand) - `make vendor` when adding new dependencies - ... (for other development tasks, check the `Makefile`) **Please** read the [CONTRIBUTING](CONTRIBUTING.md) documentation, it will tell you about internal structures and concepts. ## License This project is under the MIT License. See the [LICENSE](LICENSE) file for the full license text. tea/build.go000066400000000000000000000006201437326320500132600ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. //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" ) tea/cmd/000077500000000000000000000000001437326320500123775ustar00rootroot00000000000000tea/cmd/admin.go000066400000000000000000000025101437326320500140140ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "code.gitea.io/tea/cmd/admin/users" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/print" "github.com/urfave/cli/v2" ) // CmdAdmin represents the namespace of admin commands. // The command itself has no functionality, but hosts subcommands. var CmdAdmin = cli.Command{ Name: "admin", Usage: "Operations requiring admin access on the Gitea instance", Aliases: []string{"a"}, Category: catMisc, Action: func(cmd *cli.Context) error { return cli.ShowSubcommandHelp(cmd) }, Subcommands: []*cli.Command{ &cmdAdminUsers, }, } var cmdAdminUsers = cli.Command{ Name: "users", Aliases: []string{"u"}, Usage: "Manage registered users", Action: func(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runAdminUserDetail(ctx, ctx.Args().First()) } return users.RunUserList(ctx) }, Subcommands: []*cli.Command{ &users.CmdUserList, }, Flags: users.CmdUserList.Flags, } func runAdminUserDetail(cmd *cli.Context, u string) error { ctx := context.InitCommand(cmd) client := ctx.Login.Client() user, _, err := client.GetUserInfo(u) if err != nil { return err } print.UserDetails(user) return nil } tea/cmd/admin/000077500000000000000000000000001437326320500134675ustar00rootroot00000000000000tea/cmd/admin/users/000077500000000000000000000000001437326320500146305ustar00rootroot00000000000000tea/cmd/admin/users/list.go000066400000000000000000000023441437326320500161350ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package users import ( "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) var userFieldsFlag = flags.FieldsFlag(print.UserFields, []string{ "id", "login", "full_name", "email", "activated", }) // CmdUserList represents a sub command of users to list users var CmdUserList = cli.Command{ Name: "list", Aliases: []string{"ls"}, Usage: "List Users", Description: "List users", Action: RunUserList, Flags: append([]cli.Flag{ userFieldsFlag, &flags.PaginationPageFlag, &flags.PaginationLimitFlag, }, flags.AllDefaultFlags...), } // RunUserList list users func RunUserList(cmd *cli.Context) error { ctx := context.InitCommand(cmd) fields, err := userFieldsFlag.GetValues(cmd) if err != nil { return err } client := ctx.Login.Client() users, _, err := client.AdminListUsers(gitea.AdminListUsersOptions{ ListOptions: ctx.GetListOptions(), }) if err != nil { return err } print.UserList(users, ctx.Output, fields) return nil } tea/cmd/autocomplete.go000066400000000000000000000072111437326320500154300ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "fmt" "io" "net/http" "os" "os/exec" "github.com/adrg/xdg" "github.com/urfave/cli/v2" ) // CmdAutocomplete manages autocompletion var CmdAutocomplete = cli.Command{ Name: "shellcompletion", Aliases: []string{"autocomplete"}, Category: catSetup, Usage: "Install shell completion for tea", Description: "Install shell completion for tea", ArgsUsage: " (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 } tea/cmd/categories.go000066400000000000000000000004411437326320500150520ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd var ( catSetup = "SETUP" catEntities = "ENTITIES" catHelpers = "HELPERS" catMisc = "MISCELLANEOUS" ) tea/cmd/clone.go000066400000000000000000000040231437326320500140250ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "fmt" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/git" "code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/utils" "github.com/urfave/cli/v2" ) // CmdRepoClone represents a sub command of repos to create a local copy var CmdRepoClone = cli.Command{ Name: "clone", Aliases: []string{"C"}, Usage: "Clone a repository locally", Description: `Clone a repository locally, without a local git installation required. The repo slug can be specified in different formats: gitea/tea tea gitea.com/gitea/tea git@gitea.com:gitea/tea https://gitea.com/gitea/tea ssh://gitea.com:22/gitea/tea When a host is specified in the repo-slug, it will override the login specified with --login. `, Category: catHelpers, Action: runRepoClone, ArgsUsage: " [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 } tea/cmd/comment.go000066400000000000000000000041121437326320500143660ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "fmt" "io/ioutil" "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: " []", 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 := ioutil.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 } tea/cmd/flags/000077500000000000000000000000001437326320500134735ustar00rootroot00000000000000tea/cmd/flags/csvflag.go000066400000000000000000000027061437326320500154540ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package flags import ( "fmt" "strings" "code.gitea.io/tea/modules/utils" "github.com/urfave/cli/v2" ) // CsvFlag is a wrapper around cli.StringFlag, with an added GetValues() method // to retrieve comma separated string values as a slice. type CsvFlag struct { cli.StringFlag AvailableFields []string } // NewCsvFlag creates a CsvFlag, while setting its usage string and default values func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag { var availableDesc string if len(availableValues) != 0 { availableDesc = " Available values:" } return &CsvFlag{ AvailableFields: availableValues, StringFlag: cli.StringFlag{ Name: name, Aliases: aliases, Value: strings.Join(defaults, ","), Usage: fmt.Sprintf(`Comma-separated list of %s.%s %s `, usage, availableDesc, strings.Join(availableValues, ",")), }, } } // GetValues returns the value of the flag, parsed as a commaseparated list func (f CsvFlag) GetValues(ctx *cli.Context) ([]string, error) { val := ctx.String(f.Name) selection := strings.Split(val, ",") if f.AvailableFields != nil && val != "" { for _, field := range selection { if !utils.Contains(f.AvailableFields, field) { return nil, fmt.Errorf("Invalid field '%s'", field) } } } return selection, nil } tea/cmd/flags/generic.go000066400000000000000000000062441437326320500154440ustar00rootroot00000000000000// Copyright 2019 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package flags import ( "github.com/urfave/cli/v2" ) // LoginFlag provides flag to specify tea login profile var LoginFlag = cli.StringFlag{ Name: "login", Aliases: []string{"l"}, Usage: "Use a different Gitea Login. Optional", } // RepoFlag provides flag to specify repository var RepoFlag = cli.StringFlag{ Name: "repo", Aliases: []string{"r"}, Usage: "Override local repository path or gitea repository slug to interact with. Optional", } // RemoteFlag provides flag to specify remote repository var RemoteFlag = cli.StringFlag{ Name: "remote", Aliases: []string{"R"}, Usage: "Discover Gitea login from remote. Optional", } // OutputFlag provides flag to specify output type var OutputFlag = cli.StringFlag{ Name: "output", Aliases: []string{"o"}, Usage: "Output format. (csv, simple, table, tsv, yaml)", } // PaginationPageFlag provides flag for pagination options var PaginationPageFlag = cli.StringFlag{ Name: "page", Aliases: []string{"p"}, Usage: "specify page, default is 1", } // PaginationLimitFlag provides flag for pagination options var PaginationLimitFlag = cli.StringFlag{ Name: "limit", Aliases: []string{"lm"}, Usage: "specify limit of items per page", } // LoginOutputFlags defines login and output flags that should // added to all subcommands and appended to the flags of the // subcommand to work around issue and provide --login and --output: // https://github.com/urfave/cli/issues/585 var LoginOutputFlags = []cli.Flag{ &LoginFlag, &OutputFlag, } // LoginRepoFlags defines login and repo flags that should // be used for all subcommands and appended to the flags of // the subcommand to work around issue and provide --login and --repo: // https://github.com/urfave/cli/issues/585 var LoginRepoFlags = []cli.Flag{ &LoginFlag, &RepoFlag, &RemoteFlag, } // AllDefaultFlags defines flags that should be available // for all subcommands working with dedicated repositories // to work around issue and provide --login, --repo and --output: // https://github.com/urfave/cli/issues/585 var AllDefaultFlags = append([]cli.Flag{ &RepoFlag, &RemoteFlag, }, LoginOutputFlags...) // NotificationFlags defines flags that should be available on notifications. var NotificationFlags = append([]cli.Flag{ NotificationStateFlag, &cli.BoolFlag{ Name: "mine", Aliases: []string{"m"}, Usage: "Show notifications across all your repositories instead of the current repository only", }, &PaginationPageFlag, &PaginationLimitFlag, }, AllDefaultFlags...) // NotificationStateFlag is a csv flag applied to all notification subcommands as filter var NotificationStateFlag = NewCsvFlag( "states", "notification states to filter by", []string{"s"}, []string{"pinned", "unread", "read"}, []string{"unread", "pinned"}, ) // FieldsFlag generates a flag selecting printable fields. // To retrieve the value, use f.GetValues() func FieldsFlag(availableFields, defaultFields []string) *CsvFlag { return NewCsvFlag("fields", "fields to print", []string{"f"}, availableFields, defaultFields) } tea/cmd/flags/issue_pr.go000066400000000000000000000076771437326320500156740ustar00rootroot00000000000000// Copyright 2019 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package flags import ( "fmt" "strings" "code.gitea.io/sdk/gitea" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/task" "github.com/araddon/dateparse" "github.com/urfave/cli/v2" ) // StateFlag provides flag to specify issue/pr state, defaulting to "open" var StateFlag = cli.StringFlag{ Name: "state", Usage: "Filter by state (all|open|closed)", DefaultText: "open", } // MilestoneFilterFlag is a CSV flag used to filter issues by milestones var MilestoneFilterFlag = NewCsvFlag( "milestones", "milestones to match issues against", []string{"m"}, nil, nil) // LabelFilterFlag is a CSV flag used to filter issues by labels var LabelFilterFlag = NewCsvFlag( "labels", "labels to match issues against", []string{"L"}, nil, nil) // PRListingFlags defines flags that should be available on pr listing flags. var PRListingFlags = append([]cli.Flag{ &StateFlag, &PaginationPageFlag, &PaginationLimitFlag, }, AllDefaultFlags...) // IssueListingFlags defines flags that should be available on issue listing flags. var IssueListingFlags = append([]cli.Flag{ &StateFlag, &cli.StringFlag{ Name: "kind", Aliases: []string{"K"}, Usage: "Whether to return `issues`, `pulls`, or `all` (you can use this to apply advanced search filters to PRs)", DefaultText: "issues", }, &cli.StringFlag{ Name: "keyword", Aliases: []string{"k"}, Usage: "Filter by search string", }, LabelFilterFlag, MilestoneFilterFlag, &cli.StringFlag{ Name: "author", Aliases: []string{"A"}, }, &cli.StringFlag{ Name: "assignee", Aliases: []string{"a"}, }, &cli.StringFlag{ Name: "mentions", Aliases: []string{"M"}, }, &cli.StringFlag{ Name: "from", Aliases: []string{"F"}, Usage: "Filter by activity after this date", }, &cli.StringFlag{ Name: "until", Aliases: []string{"u"}, Usage: "Filter by activity before this date", }, &PaginationPageFlag, &PaginationLimitFlag, }, AllDefaultFlags...) // IssuePREditFlags defines flags for properties of issues and PRs var IssuePREditFlags = append([]cli.Flag{ &cli.StringFlag{ Name: "title", Aliases: []string{"t"}, }, &cli.StringFlag{ Name: "description", Aliases: []string{"d"}, }, &cli.StringFlag{ Name: "assignees", Aliases: []string{"a"}, Usage: "Comma-separated list of usernames to assign", }, &cli.StringFlag{ Name: "labels", Aliases: []string{"L"}, Usage: "Comma-separated list of labels to assign", }, &cli.StringFlag{ Name: "deadline", Aliases: []string{"D"}, Usage: "Deadline timestamp to assign", }, &cli.StringFlag{ Name: "milestone", Aliases: []string{"m"}, Usage: "Milestone to assign", }, }, LoginRepoFlags...) // GetIssuePREditFlags parses all IssuePREditFlags func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) { opts := gitea.CreateIssueOption{ Title: ctx.String("title"), Body: ctx.String("description"), Assignees: strings.Split(ctx.String("assignees"), ","), } var err error date := ctx.String("deadline") if date != "" { t, err := dateparse.ParseAny(date) if err != nil { return nil, err } opts.Deadline = &t } client := ctx.Login.Client() labelNames := strings.Split(ctx.String("labels"), ",") if len(labelNames) != 0 { if client == nil { client = ctx.Login.Client() } if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil { return nil, err } } if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 { if client == nil { client = ctx.Login.Client() } ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName) if err != nil { return nil, fmt.Errorf("Milestone '%s' not found", milestoneName) } opts.Milestone = ms.ID } return &opts, nil } tea/cmd/issues.go000066400000000000000000000036541437326320500142510ustar00rootroot00000000000000// 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" "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/v2" ) // CmdIssues represents to login a gitea server. var CmdIssues = cli.Command{ Name: "issues", Aliases: []string{"issue", "i"}, Category: catEntities, Usage: "List, create and update issues", Description: `Lists issues when called without argument. If issue index is provided, will show it in detail.`, ArgsUsage: "[]", Action: runIssues, Subcommands: []*cli.Command{ &issues.CmdIssuesList, &issues.CmdIssuesCreate, &issues.CmdIssuesReopen, &issues.CmdIssuesClose, }, Flags: append([]cli.Flag{ &cli.BoolFlag{ Name: "comments", Usage: "Whether to display comments (will prompt if not provided & run interactively)", }, }, issues.CmdIssuesList.Flags...), } func runIssues(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runIssueDetail(ctx, ctx.Args().First()) } return issues.RunIssuesList(ctx) } func runIssueDetail(cmd *cli.Context, index string) error { ctx := context.InitCommand(cmd) ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) idx, err := utils.ArgToIndex(index) if err != nil { return err } client := ctx.Login.Client() issue, _, err := client.GetIssue(ctx.Owner, ctx.Repo, idx) if err != nil { return err } reactions, _, err := client.GetIssueReactions(ctx.Owner, ctx.Repo, idx) if err != nil { return err } print.IssueDetails(issue, reactions) if issue.Comments > 0 { err = interact.ShowCommentsMaybeInteractive(ctx, idx, issue.Comments) if err != nil { return fmt.Errorf("error loading comments: %v", err) } } return nil } tea/cmd/issues/000077500000000000000000000000001437326320500137125ustar00rootroot00000000000000tea/cmd/issues/close.go000066400000000000000000000025441437326320500153530ustar00rootroot00000000000000// 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 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 an issue to 'closed'", Description: `Change state of an issue to 'closed'`, ArgsUsage: "", 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) } index, err := utils.ArgToIndex(ctx.Args().First()) if err != nil { return err } issue, _, err := ctx.Login.Client().EditIssue(ctx.Owner, ctx.Repo, index, opts) if err != nil { return err } print.IssueDetails(issue, nil) return nil } tea/cmd/issues/create.go000066400000000000000000000021441437326320500155050ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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.IssuePREditFlags, } 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.GetIssuePREditFlags(ctx) if err != nil { return err } return task.CreateIssue( ctx.Login, ctx.Owner, ctx.Repo, *opts, ) } tea/cmd/issues/list.go000066400000000000000000000052521437326320500152200ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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", }) // 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) 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 default: return fmt.Errorf("unknown state '%s'", ctx.String("state")) } kind := gitea.IssueTypeIssue switch ctx.String("kind") { case "", "issues", "issue": kind = gitea.IssueTypeIssue case "pulls", "pull", "pr": kind = gitea.IssueTypePull case "all": kind = gitea.IssueTypeAll default: return fmt.Errorf("unknown kind '%s'", ctx.String("kind")) } var err error var from, until time.Time if ctx.IsSet("from") { from, err = dateparse.ParseLocal(ctx.String("from")) if err != nil { return err } } if ctx.IsSet("until") { until, err = dateparse.ParseLocal(ctx.String("until")) if err != nil { return err } } // ignore error, as we don't do any input validation on these flags labels, _ := flags.LabelFilterFlag.GetValues(cmd) milestones, _ := flags.MilestoneFilterFlag.GetValues(cmd) issues, _, err := ctx.Login.Client().ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{ 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 } fields, err := issueFieldsFlag.GetValues(cmd) if err != nil { return err } print.IssuesPullsList(issues, ctx.Output, fields) return nil } tea/cmd/issues/reopen.go000066400000000000000000000013451437326320500155340ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 an issue to 'open'", Description: `Change state of an issue to 'open'`, ArgsUsage: "", Action: func(ctx *cli.Context) error { var s = gitea.StateOpen return editIssueState(ctx, gitea.EditIssueOption{State: &s}) }, Flags: flags.AllDefaultFlags, } tea/cmd/labels.go000066400000000000000000000017351437326320500141760ustar00rootroot00000000000000// 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 ( "fmt" "code.gitea.io/tea/cmd/labels" "github.com/urfave/cli/v2" ) // CmdLabels represents to operate repositories' labels. var CmdLabels = cli.Command{ Name: "labels", Aliases: []string{"label"}, Category: catEntities, Usage: "Manage issue labels", Description: `Manage issue labels`, ArgsUsage: " ", // command does not accept arguments Action: runLabels, Subcommands: []*cli.Command{ &labels.CmdLabelsList, &labels.CmdLabelCreate, &labels.CmdLabelUpdate, &labels.CmdLabelDelete, }, Flags: labels.CmdLabelsList.Flags, } func runLabels(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runLabelsDetails(ctx) } return labels.RunLabelsList(ctx) } func runLabelsDetails(ctx *cli.Context) error { return fmt.Errorf("Not yet implemented") } tea/cmd/labels/000077500000000000000000000000001437326320500136415ustar00rootroot00000000000000tea/cmd/labels/create.go000066400000000000000000000047331437326320500154420ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/labels/create_test.go000066400000000000000000000025021437326320500164710ustar00rootroot00000000000000// 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 labels import ( "bufio" "strings" "testing" "github.com/stretchr/testify/assert" ) func TestParseLabelLine(t *testing.T) { const labels = `#ededed in progress #fbca04 kind/breaking ; breaking label #fc2929 kind/bug #c5def5 kind/deployment ; deployment label #000000 in progress ; in progress label ` scanner := bufio.NewScanner(strings.NewReader(labels)) var i = 1 for scanner.Scan() { line := scanner.Text() color, name, description := splitLabelLine(line) switch i { case 1: assert.EqualValues(t, "#ededed", color) assert.EqualValues(t, "in progress", name) case 2: assert.EqualValues(t, "#fbca04", color) assert.EqualValues(t, "kind/breaking", name) assert.EqualValues(t, "breaking label", description) case 3: assert.EqualValues(t, "#fc2929", color) assert.EqualValues(t, "kind/bug", name) case 4: assert.EqualValues(t, "#c5def5", color) assert.EqualValues(t, "kind/deployment", name) assert.EqualValues(t, "deployment label", description) case 5: assert.EqualValues(t, "#000000", color) assert.EqualValues(t, "in progress", name) assert.EqualValues(t, "in progress label", description) } i++ } } tea/cmd/labels/delete.go000066400000000000000000000016601437326320500154350ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/labels/list.go000066400000000000000000000025731437326320500151520ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/labels/update.go000066400000000000000000000030761437326320500154600ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/login.go000066400000000000000000000021511437326320500140350ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "fmt" "code.gitea.io/tea/cmd/login" "code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/print" "github.com/urfave/cli/v2" ) // CmdLogin represents to login a gitea server. var CmdLogin = cli.Command{ Name: "logins", Aliases: []string{"login"}, Category: catSetup, Usage: "Log in to a Gitea server", Description: `Log in to a Gitea server`, ArgsUsage: "[]", Action: runLogins, Subcommands: []*cli.Command{ &login.CmdLoginList, &login.CmdLoginAdd, &login.CmdLoginEdit, &login.CmdLoginDelete, &login.CmdLoginSetDefault, }, } func runLogins(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runLoginDetail(ctx.Args().First()) } return login.RunLoginList(ctx) } func runLoginDetail(name string) error { l := config.GetLoginByName(name) if l == nil { fmt.Printf("Login '%s' do not exist\n\n", name) return nil } print.LoginDetails(l) return nil } tea/cmd/login/000077500000000000000000000000001437326320500135075ustar00rootroot00000000000000tea/cmd/login/add.go000066400000000000000000000040231437326320500145650ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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.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: "ssh-key", Aliases: []string{"s"}, Usage: "Path to a SSH key to use, overrides auto-discovery", }, &cli.BoolFlag{ Name: "insecure", Aliases: []string{"i"}, Usage: "Disable TLS verification", }, }, Action: runLoginAdd, } func runLoginAdd(ctx *cli.Context) error { // if no args create login interactive if ctx.NumFlags() == 0 { return interact.CreateLogin() } // else use args to add login return task.CreateLogin( ctx.String("name"), ctx.String("token"), ctx.String("user"), ctx.String("password"), ctx.String("ssh-key"), ctx.String("url"), ctx.Bool("insecure")) } tea/cmd/login/default.go000066400000000000000000000015651437326320500154710ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "", 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) } tea/cmd/login/delete.go000066400000000000000000000017031437326320500153010ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "", 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) } tea/cmd/login/edit.go000066400000000000000000000013601437326320500147630ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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()) } tea/cmd/login/list.go000066400000000000000000000015411437326320500150120ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/logout.go000066400000000000000000000010251437326320500142350ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "code.gitea.io/tea/cmd/login" "github.com/urfave/cli/v2" ) // CmdLogout represents to logout a gitea server. var CmdLogout = cli.Command{ Name: "logout", Category: catSetup, Usage: "Log out from a Gitea server", Description: `Log out from a Gitea server`, ArgsUsage: "", Action: login.RunLoginDelete, } tea/cmd/milestones.go000066400000000000000000000027021437326320500151110ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "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: "[]", 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 } tea/cmd/milestones/000077500000000000000000000000001437326320500145615ustar00rootroot00000000000000tea/cmd/milestones/close.go000066400000000000000000000015271437326320500162220ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 an milestone to 'closed'", Description: `Change state of an milestone to 'closed'`, ArgsUsage: "", 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...), } tea/cmd/milestones/create.go000066400000000000000000000036601437326320500163600ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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, ) } tea/cmd/milestones/delete.go000066400000000000000000000016131437326320500163530ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "", 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 } tea/cmd/milestones/issues.go000066400000000000000000000112371437326320500164270ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "", 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: " ", 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: " ", 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 } tea/cmd/milestones/list.go000066400000000000000000000035061437326320500160670ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/milestones/reopen.go000066400000000000000000000022421437326320500164000ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package milestones import ( "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "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 an milestone to 'open'", Description: `Change state of an milestone to 'open'`, ArgsUsage: "", 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}) client := ctx.Login.Client() state := gitea.StateOpen if close { state = gitea.StateClosed } _, _, err := client.EditMilestoneByName(ctx.Owner, ctx.Repo, ctx.Args().First(), gitea.EditMilestoneOption{ State: &state, Title: ctx.Args().First(), }) return err } tea/cmd/notifications.go000066400000000000000000000016471437326320500156070ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "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, } tea/cmd/notifications/000077500000000000000000000000001437326320500152505ustar00rootroot00000000000000tea/cmd/notifications/list.go000066400000000000000000000054711437326320500165610ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package notifications import ( "log" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) var notifyFieldsFlag = flags.FieldsFlag(print.NotificationFields, []string{ "id", "status", "index", "type", "state", "title", }) var notifyTypeFlag = flags.NewCsvFlag("types", "subject types to filter by", []string{"t"}, []string{"issue", "pull", "repository", "commit"}, nil) // CmdNotificationsList represents a sub command of notifications to list notifications var CmdNotificationsList = cli.Command{ Name: "ls", Aliases: []string{"list"}, Usage: "List notifications", Description: `List notifications`, ArgsUsage: " ", // command does not accept arguments Action: RunNotificationsList, Flags: append([]cli.Flag{ notifyFieldsFlag, notifyTypeFlag, }, flags.NotificationFlags...), } // RunNotificationsList list notifications func RunNotificationsList(ctx *cli.Context) error { var states []gitea.NotifyStatus statesStr, err := flags.NotificationStateFlag.GetValues(ctx) if err != nil { return err } for _, s := range statesStr { states = append(states, gitea.NotifyStatus(s)) } var types []gitea.NotifySubjectType typesStr, err := notifyTypeFlag.GetValues(ctx) if err != nil { return err } for _, t := range typesStr { types = append(types, gitea.NotifySubjectType(t)) } return listNotifications(ctx, states, types) } // listNotifications will get the notifications based on status and subject type func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects []gitea.NotifySubjectType) error { var news []*gitea.NotificationThread var err error ctx := context.InitCommand(cmd) client := ctx.Login.Client() all := ctx.Bool("mine") // This enforces pagination (see https://github.com/go-gitea/gitea/issues/16733) listOpts := ctx.GetListOptions() if listOpts.Page == 0 { listOpts.Page = 1 } fields, err := notifyFieldsFlag.GetValues(cmd) if err != nil { return err } if all { // add repository to the default fields if !cmd.IsSet("fields") { fields = append(fields, "repository") } news, _, err = client.ListNotifications(gitea.ListNotificationOptions{ ListOptions: listOpts, Status: status, SubjectTypes: subjects, }) } else { ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) news, _, err = client.ListRepoNotifications(ctx.Owner, ctx.Repo, gitea.ListNotificationOptions{ ListOptions: listOpts, Status: status, SubjectTypes: subjects, }) } if err != nil { log.Fatal(err) } print.NotificationsList(news, ctx.Output, fields) return nil } tea/cmd/notifications/mark_as.go000066400000000000000000000105351437326320500172200ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package notifications import ( "fmt" "code.gitea.io/sdk/gitea" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/utils" "github.com/urfave/cli/v2" ) // CmdNotificationsMarkRead represents a sub command of notifications to list read notifications var CmdNotificationsMarkRead = cli.Command{ Name: "read", Aliases: []string{"r"}, Usage: "Mark all filtered or a specific notification as read", Description: "Mark all filtered or a specific notification as read", ArgsUsage: "[all | ]", 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 | ]", 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 | ]", 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 | ]", 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 } tea/cmd/open.go000066400000000000000000000040301437326320500136640ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "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 } u := path.Join(ctx.Login.URL, ctx.Owner, ctx.Repo, suffix) return open.Run(u) } tea/cmd/organizations.go000066400000000000000000000023631437326320500156210ustar00rootroot00000000000000// 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 ( "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: "[]", 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 } tea/cmd/organizations/000077500000000000000000000000001437326320500152665ustar00rootroot00000000000000tea/cmd/organizations/create.go000066400000000000000000000044601437326320500170640ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package organizations import ( "fmt" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) // CmdOrganizationCreate represents a sub command of organizations to delete a given user organization var CmdOrganizationCreate = cli.Command{ Name: "create", Aliases: []string{"c"}, Usage: "Create an organization", Description: "Create an organization", Action: RunOrganizationCreate, ArgsUsage: "", 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 } tea/cmd/organizations/delete.go000066400000000000000000000022571437326320500170650ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "", 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 } tea/cmd/organizations/list.go000066400000000000000000000023131437326320500165670ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/pulls.go000066400000000000000000000046741437326320500141000ustar00rootroot00000000000000// 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" "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" "github.com/urfave/cli/v2" ) // CmdPulls is the main command to operate on PRs var CmdPulls = cli.Command{ Name: "pulls", Aliases: []string{"pull", "pr"}, Category: catEntities, Usage: "Manage and checkout pull requests", Description: `Lists PRs when called without argument. If PR index is provided, will show it in detail.`, ArgsUsage: "[]", Action: runPulls, Flags: append([]cli.Flag{ &cli.BoolFlag{ Name: "comments", Usage: "Whether to display comments (will prompt if not provided & run interactively)", }, }, pulls.CmdPullsList.Flags...), Subcommands: []*cli.Command{ &pulls.CmdPullsList, &pulls.CmdPullsCheckout, &pulls.CmdPullsClean, &pulls.CmdPullsCreate, &pulls.CmdPullsClose, &pulls.CmdPullsReopen, &pulls.CmdPullsReview, &pulls.CmdPullsApprove, &pulls.CmdPullsReject, &pulls.CmdPullsMerge, }, } func runPulls(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runPullDetail(ctx, ctx.Args().First()) } return pulls.RunPullsList(ctx) } func runPullDetail(cmd *cli.Context, index string) error { ctx := context.InitCommand(cmd) ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) idx, err := utils.ArgToIndex(index) if err != nil { return err } client := ctx.Login.Client() pr, _, err := client.GetPullRequest(ctx.Owner, ctx.Repo, idx) if err != nil { return err } if err := workaround.FixPullHeadSha(client, pr); err != nil { return err } reviews, _, err := client.ListPullReviews(ctx.Owner, ctx.Repo, idx, gitea.ListPullReviewsOptions{ ListOptions: gitea.ListOptions{Page: -1}, }) if err != nil { fmt.Printf("error while loading reviews: %v\n", err) } ci, _, err := client.GetCombinedStatus(ctx.Owner, ctx.Repo, pr.Head.Sha) if err != nil { fmt.Printf("error while loading CI: %v\n", err) } print.PullDetails(pr, reviews, ci) if pr.Comments > 0 { err = interact.ShowCommentsMaybeInteractive(ctx, idx, pr.Comments) if err != nil { fmt.Printf("error loading comments: %v\n", err) } } return nil } tea/cmd/pulls/000077500000000000000000000000001437326320500135365ustar00rootroot00000000000000tea/cmd/pulls/approve.go000066400000000000000000000021441437326320500155420ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: " []", 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, } tea/cmd/pulls/checkout.go000066400000000000000000000025001437326320500156670ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "", 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) } tea/cmd/pulls/clean.go000066400000000000000000000025171437326320500151540ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "", 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) } tea/cmd/pulls/close.go000066400000000000000000000013021437326320500151660ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 a pull request to 'closed'", Description: `Change state of a pull request to 'closed'`, ArgsUsage: "", Action: func(ctx *cli.Context) error { var s = gitea.StateClosed return editPullState(ctx, gitea.EditPullRequestOption{State: &s}) }, Flags: flags.AllDefaultFlags, } tea/cmd/pulls/create.go000066400000000000000000000025251437326320500153340ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 :", }, &cli.StringFlag{ Name: "base", Aliases: []string{"b"}, Usage: "Branch name of the PR target (default is repos default branch)", }, }, flags.IssuePREditFlags...), } 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.GetIssuePREditFlags(ctx) if err != nil { return err } return task.CreatePull( ctx, ctx.String("base"), ctx.String("head"), opts, ) } tea/cmd/pulls/edit.go000066400000000000000000000016571437326320500150230ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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") } index, err := utils.ArgToIndex(ctx.Args().First()) if err != nil { return err } pr, _, err := ctx.Login.Client().EditPullRequest(ctx.Owner, ctx.Repo, index, opts) if err != nil { return err } print.PullDetails(pr, nil, nil) return nil } tea/cmd/pulls/list.go000066400000000000000000000030301437326320500150340ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/pulls/merge.go000066400000000000000000000032401437326320500151630ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package pulls import ( "fmt" "code.gitea.io/sdk/gitea" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/utils" "github.com/urfave/cli/v2" ) // CmdPullsMerge merges a PR var CmdPullsMerge = cli.Command{ Name: "merge", Aliases: []string{"m"}, Usage: "Merge a pull request", Description: "Merge a pull request", ArgsUsage: "", Flags: append([]cli.Flag{ &cli.StringFlag{ Name: "style", Aliases: []string{"s"}, Usage: "Kind of merge to perform: merge, rebase, squash, rebase-merge", Value: "merge", }, &cli.StringFlag{ Name: "title", Aliases: []string{"t"}, Usage: "Merge commit title", }, &cli.StringFlag{ Name: "message", Aliases: []string{"m"}, Usage: "Merge commit message", }, }, flags.AllDefaultFlags...), Action: func(cmd *cli.Context) error { ctx := context.InitCommand(cmd) ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) if ctx.Args().Len() != 1 { return fmt.Errorf("Must specify a PR index") } idx, err := utils.ArgToIndex(ctx.Args().First()) if err != nil { return err } success, _, err := ctx.Login.Client().MergePullRequest(ctx.Owner, ctx.Repo, idx, gitea.MergePullRequestOption{ Style: gitea.MergeStyle(ctx.String("style")), Title: ctx.String("title"), Message: ctx.String("message"), }) if err != nil { return err } if !success { return fmt.Errorf("Failed to merge PR. Is it still open?") } return nil }, } tea/cmd/pulls/reject.go000066400000000000000000000021531437326320500153420ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: " ", 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, } tea/cmd/pulls/reopen.go000066400000000000000000000013421437326320500153550ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 a pull request to 'open'", Description: `Change state of a pull request to 'open'`, ArgsUsage: "", Action: func(ctx *cli.Context) error { var s = gitea.StateOpen return editPullState(ctx, gitea.EditPullRequestOption{State: &s}) }, Flags: flags.AllDefaultFlags, } tea/cmd/pulls/review.go000066400000000000000000000017411437326320500153710ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "", 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, } tea/cmd/releases.go000066400000000000000000000014721437326320500145350ustar00rootroot00000000000000// Copyright 2018 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/cmd/releases" "github.com/urfave/cli/v2" ) // CmdReleases represents to login a gitea server. // ToDo: ReleaseDetails var CmdReleases = cli.Command{ Name: "releases", Aliases: []string{"release", "r"}, Category: catEntities, Usage: "Manage releases", Description: "Manage releases", ArgsUsage: " ", // command does not accept arguments Action: releases.RunReleasesList, Subcommands: []*cli.Command{ &releases.CmdReleaseList, &releases.CmdReleaseCreate, &releases.CmdReleaseDelete, &releases.CmdReleaseEdit, }, Flags: flags.AllDefaultFlags, } tea/cmd/releases/000077500000000000000000000000001437326320500142025ustar00rootroot00000000000000tea/cmd/releases/create.go000066400000000000000000000052561437326320500160040ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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: "[]", 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 } tea/cmd/releases/delete.go000066400000000000000000000030431437326320500157730ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 a release", Description: `Delete a release`, ArgsUsage: "", 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() tag := ctx.Args().First() if len(tag) == 0 { fmt.Println("Release tag needed to delete") return nil } 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 } _, 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 } tea/cmd/releases/edit.go000066400000000000000000000043111437326320500154550ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 a release", Description: `Edit a release`, ArgsUsage: "", 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() tag := ctx.Args().First() if len(tag) == 0 { fmt.Println("Release tag needed to edit") return nil } release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client) if err != nil { return err } 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") } _, _, 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, }) return err } tea/cmd/releases/list.go000066400000000000000000000031631437326320500155070ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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") } tea/cmd/repos.go000066400000000000000000000027201437326320500140570ustar00rootroot00000000000000// 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 ( "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" "github.com/urfave/cli/v2" ) // CmdRepos represents to login a gitea server. var CmdRepos = cli.Command{ Name: "repos", Aliases: []string{"repo"}, Category: catEntities, Usage: "Show repository details", Description: "Show repository details", ArgsUsage: "[/]", Action: runRepos, Subcommands: []*cli.Command{ &repos.CmdReposList, &repos.CmdReposSearch, &repos.CmdRepoCreate, &repos.CmdRepoCreateFromTemplate, &repos.CmdRepoFork, }, Flags: repos.CmdReposListFlags, } func runRepos(ctx *cli.Context) error { if ctx.Args().Len() == 1 { return runRepoDetail(ctx, ctx.Args().First()) } return repos.RunReposList(ctx) } func runRepoDetail(cmd *cli.Context, path string) error { ctx := context.InitCommand(cmd) client := ctx.Login.Client() repoOwner, repoName := utils.GetOwnerAndRepo(path, ctx.Owner) repo, _, err := client.GetRepo(repoOwner, repoName) if err != nil { return err } topics, _, err := client.ListRepoTopics(repoOwner, repoName, gitea.ListRepoTopicsOptions{}) if err != nil { return err } print.RepoDetails(repo, topics) return nil } tea/cmd/repos/000077500000000000000000000000001437326320500135275ustar00rootroot00000000000000tea/cmd/repos/create.go000066400000000000000000000070621437326320500153260ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/repos/create_from_template.go000066400000000000000000000057431437326320500202500ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package repos import ( "fmt" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/utils" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) // CmdRepoCreateFromTemplate represents a sub command of repos to generate one from a template repo var CmdRepoCreateFromTemplate = cli.Command{ Name: "create-from-template", Aliases: []string{"ct"}, Usage: "Create a repository based on an existing template", Description: "Create a repository based on an existing template", Action: runRepoCreateFromTemplate, Flags: append([]cli.Flag{ &cli.StringFlag{ Name: "template", Aliases: []string{"t"}, Required: true, Usage: "source template to copy from", }, &cli.StringFlag{ Name: "name", Aliases: []string{"n"}, Required: true, Usage: "name of new repo", }, &cli.StringFlag{ Name: "owner", Aliases: []string{"O"}, Usage: "name of repo owner", }, &cli.BoolFlag{ Name: "private", Usage: "make new repo private", }, &cli.StringFlag{ Name: "description", Aliases: []string{"desc"}, Usage: "add custom description to repo", }, &cli.BoolFlag{ Name: "content", Value: true, Usage: "copy git content from template", }, &cli.BoolFlag{ Name: "githooks", Value: true, Usage: "copy git hooks from template", }, &cli.BoolFlag{ Name: "avatar", Value: true, Usage: "copy repo avatar from template", }, &cli.BoolFlag{ Name: "labels", Value: true, Usage: "copy repo labels from template", }, &cli.BoolFlag{ Name: "topics", Value: true, Usage: "copy topics from template", }, &cli.BoolFlag{ Name: "webhooks", Usage: "copy webhooks from template", }, }, flags.LoginOutputFlags...), } func runRepoCreateFromTemplate(cmd *cli.Context) error { ctx := context.InitCommand(cmd) client := ctx.Login.Client() templateOwner, templateRepo := utils.GetOwnerAndRepo(ctx.String("template"), ctx.Login.User) owner := ctx.Login.User if ctx.IsSet("owner") { owner = ctx.String("owner") } opts := gitea.CreateRepoFromTemplateOption{ Name: ctx.String("name"), Owner: owner, Description: ctx.String("description"), Private: ctx.Bool("private"), GitContent: ctx.Bool("content"), GitHooks: ctx.Bool("githooks"), Avatar: ctx.Bool("avatar"), Labels: ctx.Bool("labels"), Topics: ctx.Bool("topics"), Webhooks: ctx.Bool("webhooks"), } repo, _, err := client.CreateRepoFromTemplate(templateOwner, templateRepo, opts) if err != nil { return err } topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{}) if err != nil { return err } print.RepoDetails(repo, topics) fmt.Printf("%s\n", repo.HTMLURL) return nil } tea/cmd/repos/flags.go000066400000000000000000000014731437326320500151570ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/repos/fork.go000066400000000000000000000027251437326320500150250ustar00rootroot00000000000000// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package repos import ( "fmt" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) // CmdRepoFork represents a sub command of repos to fork an existing repo var CmdRepoFork = cli.Command{ Name: "fork", Aliases: []string{"f"}, Usage: "Fork an existing repository", Description: "Create a repository from an existing repo", ArgsUsage: " ", // command does not accept arguments Action: runRepoFork, Flags: append([]cli.Flag{ &cli.StringFlag{ Name: "owner", Aliases: []string{"O"}, Usage: "name of fork's owner, defaults to current user", }, }, flags.LoginRepoFlags...), } func runRepoFork(cmd *cli.Context) error { ctx := context.InitCommand(cmd) ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) client := ctx.Login.Client() opts := gitea.CreateForkOption{} if ctx.IsSet("owner") { owner := ctx.String("owner") opts.Organization = &owner } repo, _, err := client.CreateFork(ctx.Owner, ctx.Repo, opts) if err != nil { return err } topics, _, err := client.ListRepoTopics(repo.Owner.UserName, repo.Name, gitea.ListRepoTopicsOptions{}) if err != nil { return err } print.RepoDetails(repo, topics) fmt.Printf("%s\n", repo.HTMLURL) return nil } tea/cmd/repos/list.go000066400000000000000000000052321437326320500150330ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package 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 } tea/cmd/repos/search.go000066400000000000000000000062551437326320500153330ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package repos import ( "fmt" "strings" "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" ) // CmdReposSearch represents a sub command of repos to find them var CmdReposSearch = cli.Command{ Name: "search", Aliases: []string{"s"}, Usage: "Find any repo on an Gitea instance", Description: "Find any repo on an Gitea instance", ArgsUsage: "[]", Action: runReposSearch, Flags: append([]cli.Flag{ &cli.BoolFlag{ // TODO: it might be nice to search for topics as an ADDITIONAL filter. // for that, we'd probably need to make multiple queries and UNION the results. Name: "topic", Aliases: []string{"t"}, Required: false, Usage: "Search for term in repo topics instead of name", }, &typeFilterFlag, &cli.StringFlag{ Name: "owner", Aliases: []string{"O"}, Required: false, Usage: "Filter by owner", }, &cli.StringFlag{ Name: "private", Required: false, Usage: "Filter private repos (true|false)", }, &cli.StringFlag{ Name: "archived", Required: false, Usage: "Filter archived repos (true|false)", }, repoFieldsFlag, &flags.PaginationPageFlag, &flags.PaginationLimitFlag, }, flags.LoginOutputFlags...), } func runReposSearch(cmd *cli.Context) error { ctx := context.InitCommand(cmd) client := ctx.Login.Client() var ownerID int64 if ctx.IsSet("owner") { // test if owner is a organisation org, _, err := client.GetOrg(ctx.String("owner")) if err != nil { // HACK: the client does not return a response on 404, so we can't check res.StatusCode if err.Error() != "404 Not Found" { return fmt.Errorf("Could not find owner: %s", err) } // if owner is no org, its a user user, _, err := client.GetUserInfo(ctx.String("owner")) if err != nil { return err } ownerID = user.ID } else { ownerID = org.ID } } var isArchived *bool if ctx.IsSet("archived") { archived := strings.ToLower(ctx.String("archived"))[:1] == "t" isArchived = &archived } var isPrivate *bool if ctx.IsSet("private") { private := strings.ToLower(ctx.String("private"))[:1] == "t" isPrivate = &private } mode, err := getTypeFilter(cmd) if err != nil { return err } var keyword string if ctx.Args().Present() { keyword = strings.Join(ctx.Args().Slice(), " ") } user, _, err := client.GetMyUserInfo() if err != nil { return err } rps, _, err := client.SearchRepos(gitea.SearchRepoOptions{ ListOptions: ctx.GetListOptions(), OwnerID: ownerID, IsPrivate: isPrivate, IsArchived: isArchived, Type: mode, Keyword: keyword, KeywordInDescription: true, KeywordIsTopic: ctx.Bool("topic"), PrioritizedByOwnerID: user.ID, }) if err != nil { return err } fields, err := repoFieldsFlag.GetValues(cmd) if err != nil { return err } print.ReposList(rps, ctx.Output, fields) return nil } tea/cmd/times.go000066400000000000000000000016641437326320500140560ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package cmd import ( "code.gitea.io/tea/cmd/times" "github.com/urfave/cli/v2" ) // CmdTrackedTimes represents the command to operate repositories' times. var CmdTrackedTimes = cli.Command{ Name: "times", Aliases: []string{"time", "t"}, Category: catEntities, Usage: "Operate on tracked times of a repository's issues & pulls", Description: `Operate on tracked times of a repository's issues & pulls. Depending on your permissions on the repository, only your own tracked times might be listed.`, ArgsUsage: "[username | #issue]", Action: times.RunTimesList, Subcommands: []*cli.Command{ ×.CmdTrackedTimesAdd, ×.CmdTrackedTimesDelete, ×.CmdTrackedTimesReset, ×.CmdTrackedTimesList, }, Flags: times.CmdTrackedTimesList.Flags, } tea/cmd/times/000077500000000000000000000000001437326320500135205ustar00rootroot00000000000000tea/cmd/times/add.go000066400000000000000000000025351437326320500146040ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package times import ( "fmt" "strings" "time" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/utils" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" ) // CmdTrackedTimesAdd represents a sub command of times to add time to an issue var CmdTrackedTimesAdd = cli.Command{ Name: "add", Aliases: []string{"a"}, Usage: "Track spent time on an issue", UsageText: "tea times add ", Description: `Track spent time on an issue Example: tea times add 1 1h25m `, Action: runTrackedTimesAdd, Flags: flags.LoginRepoFlags, } func runTrackedTimesAdd(cmd *cli.Context) error { ctx := context.InitCommand(cmd) ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) if ctx.Args().Len() < 2 { return fmt.Errorf("No issue or duration specified.\nUsage:\t%s", ctx.Command.UsageText) } issue, err := utils.ArgToIndex(ctx.Args().First()) if err != nil { return err } duration, err := time.ParseDuration(strings.Join(ctx.Args().Tail(), "")) if err != nil { return err } _, _, err = ctx.Login.Client().AddTime(ctx.Owner, ctx.Repo, issue, gitea.AddTimeOption{ Time: int64(duration.Seconds()), }) return err } tea/cmd/times/delete.go000066400000000000000000000023441437326320500153140ustar00rootroot00000000000000// Copyright 2020 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package times import ( "fmt" "strconv" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/utils" "github.com/urfave/cli/v2" ) // CmdTrackedTimesDelete is a sub command of CmdTrackedTimes, and removes time from an issue var CmdTrackedTimesDelete = cli.Command{ Name: "delete", Aliases: []string{"rm"}, Usage: "Delete a single tracked time on an issue", UsageText: "tea times delete