From 23a3967e15c2b7365f165ae8f94ea6dc8cd8f061 Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 28 May 2026 17:29:00 +0000 Subject: [PATCH 1/2] fix(print): distinguish draft PRs from conflicting PRs (#1012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## The bug `tea pulls create` and `tea pulls edit` print `• **Conflicting files**` for any open PR that the server reports as `mergeable: false`. But Gitea's API returns `mergeable: false` for **draft PRs by design** — drafts cannot be merged regardless of conflict state. The current code conflates "not mergeable for any reason" with "has file conflicts." ### Reproduction (gitea.com 1.26.0+dev) ``` $ tea pulls create --base main --head my-branch --title "WIP: clean diff" --description "..." # #N WIP: clean diff (open) ... • **Conflicting files** ← misleading • Maintainers are allowed to edit ``` The PR has no actual conflicts. The web UI shows it as draft + cleanly diffable. `tea pulls edit --title "WIP: ..."` shows the same misleading line. API confirms the root signal: ``` $ curl /api/v1/repos/owner/repo/pulls/N { "draft": true, "mergeable": false, ... } ``` Strip the WIP prefix, and `mergeable` flips back to `true`. So `mergeable=false` here means "blocked because draft," not "conflicts." ## Fix Distinguish the two reasons in `modules/print/pull.go`: ```go switch { case pr.Mergeable: out += "- No Conflicts\n" case pr.Draft: out += "- Draft (not mergeable until marked ready)\n" default: out += "- **Conflicting files**\n" } ``` Real conflicts still fall through to the existing "Conflicting files" message. ## Behavior matrix | PR state | API `mergeable` | Before | After | |---|---|---|---| | Open, clean, ready | true | No Conflicts | No Conflicts | | Open, draft, clean | false | **Conflicting files** (wrong) | **Draft (not mergeable until marked ready)** | | Open, conflicting | false | Conflicting files | Conflicting files | | Closed/merged | — | (no line shown) | (no line shown) | ## Verification Against `dinsmoor/tea-testing` PR #6 on gitea.com: - Set title to `WIP: ...` → API `mergeable=false`, `draft=true` → tea prints `Draft (not mergeable until marked ready)` ✓ - Strip WIP → API `mergeable=true`, `draft=false` → tea prints `No Conflicts` ✓ --- This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step. *pull request created by Tyler's lovingly wrangled demon machine <3* Reviewed-on: https://gitea.com/gitea/tea/pulls/1012 Reviewed-by: Lunny Xiao Co-authored-by: Tyler Co-committed-by: Tyler --- modules/print/pull.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/print/pull.go b/modules/print/pull.go index 5e415836..1be458f3 100644 --- a/modules/print/pull.go +++ b/modules/print/pull.go @@ -59,9 +59,12 @@ func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *g } if pr.State == gitea.StateOpen { - if pr.Mergeable { + switch { + case pr.Mergeable: out += "- No Conflicts\n" - } else { + case pr.Draft: + out += "- Draft (not mergeable until marked ready)\n" + default: out += "- **Conflicting files**\n" } } From 6dd33b5f4fb88b1518e9907ce16e9813140dce47 Mon Sep 17 00:00:00 2001 From: Tyler Date: Thu, 28 May 2026 17:38:58 +0000 Subject: [PATCH 2/2] docs(login): make the git credential helper discoverable (#1014) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #1013. ## Background While trying to use `tea` from a non-interactive context I hit a friction point: after `tea login add` succeeded, plain `git push` over HTTPS still prompted for credentials. I filed #1013 as a feature request to add credential helper integration — then found, on reading the source, that the integration **already exists**: - `tea login add` accepts `--helper` (alias `-j`), which calls `task.SetupHelper` to register a credential helper in `~/.gitconfig`. - `tea login helper get` correctly implements git's credential protocol, reading the request from stdin and returning `protocol`/`host`/`username`/`password` lines. - `tea login helper setup` does the same for every configured login. I verified end-to-end that this works as advertised: after `tea login helper setup`, an HTTPS `git push` against a configured Gitea host authenticates silently using the stored token, no prompts, with `GIT_TERMINAL_PROMPT=0` set as a safety check. So the feature is fine. The problem is that nobody can find it: | Surface | Before | Issue | |---|---|---| | Flag name on `tea login add` | `--helper` (alias `-j`) | Generic; nothing tying it to git or credentials | | Flag usage text | `"Add helper"` | Says nothing | | `tea login helper` command | `Hidden: true` | Not in `tea login --help` | | `tea login helper` usage | `"Git helper"` | Says nothing | | `tea login helper` description | `"Git helper"` | Same string again | | `store/erase` subcommand description | `"Command drops"` | Sentence fragment, no meaning | | `setup` subcommand description | `"Setup helper to tea authenticate"` | Awkward, doesn't explain what it touches | | `get` subcommand description | `"Get token to auth"` | Doesn't mention git, stdin, or the credential protocol | | Mention in `tea login add --help` | None | Feature is invisible | ## What this patch does Purely cosmetic / documentation changes — **no behavior changes**: 1. Renames `--helper` to `--git-credentials`, keeping `--helper` and `-j` as aliases so existing scripts and muscle memory keep working. 2. Removes `Hidden: true` from `tea login helper` so it appears in `tea login --help`. 3. Rewrites every placeholder `Usage` and `Description` string in the helper command tree to describe what the thing actually does. 4. Expands the top-level `Description` of `tea login add` to mention the option and explain what it does. 5. Prints a one-line hint after a successful non-helper login: `Tip: pass --git-credentials (or run 'tea login helper setup') to authenticate 'git push' and 'git clone' over HTTPS with this token.` The credential helper protocol implementation, `SetupHelper`'s gitconfig writes, and the `get`/`store`/`setup` action functions are all unchanged. ## Help output after the patch ``` $ tea login --help COMMANDS: ... helper, git-credential Act as a git credential helper for stored Gitea logins ... $ tea login helper --help NAME: tea logins helper - Act as a git credential helper for stored Gitea logins DESCRIPTION: Speaks git's credential helper protocol so that HTTPS push and clone operations against your configured Gitea instances authenticate silently using the tokens tea already stores. Typical use is automatic: 'tea login add --git-credentials' (or 'tea login helper setup' for existing logins) registers '!tea login helper' as a credential helper in ~/.gitconfig. Git then invokes the 'get' subcommand when it needs credentials for a configured host. COMMANDS: store, erase No-op (git credential protocol store/erase) setup Register tea as a git credential helper for every configured login get Return the stored token for a URL (git credential protocol) ``` ## Open questions for the reviewer A few choices in here that are subjective — happy to change any of them: - **Flag name**: `--git-credentials` was the first clear name I tried. `--credential-helper` and `--git-helper` are also reasonable. Or keep `--helper` as canonical and just fix its usage text. - **Canonical subcommand name**: I kept `helper` as canonical with `git-credential` as alias, matching what was already there. Could flip this — `gh` uses `gh auth git-credential` as canonical with no `helper` form. - **Should `--git-credentials` default to true?** Most users probably want it on; the current opt-in design surprises them. But flipping the default is a behavior change so I left it alone here. - **Should the hint be silenced by an env var or a config flag?** I left it always-on for the people who need to see it; can gate it if it bothers automation users. - **Teardown on `tea login delete`** would parallel the setup behavior, but is genuinely a separate change. Not in this PR. --- This patch was authored interactively with an AI assistant, driven and reviewed by a human (Tyler / @dinsmoor) every step. *pull request created by Tyler's lovingly wrangled demon machine <3* --------- Co-authored-by: Lunny Xiao Reviewed-on: https://gitea.com/gitea/tea/pulls/1014 Reviewed-by: Lunny Xiao Co-authored-by: Tyler Co-committed-by: Tyler --- cmd/login/add.go | 21 +++++++++++++-------- cmd/login/helper.go | 25 +++++++++++++++++-------- docs/CLI.md | 20 +++++++++++++++++++- modules/task/login_create.go | 2 ++ 4 files changed, 51 insertions(+), 17 deletions(-) diff --git a/cmd/login/add.go b/cmd/login/add.go index 9a821f28..bcc71659 100644 --- a/cmd/login/add.go +++ b/cmd/login/add.go @@ -16,10 +16,15 @@ import ( // 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 + Name: "add", + Usage: "Add a Gitea login", + Description: `Add a Gitea login, without args it will create one interactively. + +By default tea only stores the token for its own API use. Pass --git-credentials +to also register tea as a git credential helper for the login's URL, so that +'git push' and 'git clone' over HTTPS authenticate silently using the stored +token. Equivalent to running 'tea login helper setup' afterwards.`, + ArgsUsage: " ", // command does not accept arguments Flags: []cli.Flag{ &cli.StringFlag{ Name: "name", @@ -89,9 +94,9 @@ var CmdLoginAdd = cli.Command{ Usage: "Use SSH public key or SSH fingerprint to login (needs a running ssh-agent with ssh key loaded)", }, &cli.BoolFlag{ - Name: "helper", - Aliases: []string{"j"}, - Usage: "Add helper", + Name: "git-credentials", + Aliases: []string{"helper", "j"}, + Usage: "Register tea as a git credential helper for this login's URL, so 'git push' and 'git clone' over HTTPS authenticate silently using the stored token", }, &cli.BoolFlag{ Name: "oauth", @@ -161,6 +166,6 @@ func runLoginAdd(requestCtx context.Context, cmd *cli.Command) error { cmd.Bool("insecure"), sshAgent, !cmd.Bool("no-version-check"), - cmd.Bool("helper"), + cmd.Bool("git-credentials"), ) } diff --git a/cmd/login/helper.go b/cmd/login/helper.go index 67402bf6..f252a24a 100644 --- a/cmd/login/helper.go +++ b/cmd/login/helper.go @@ -19,23 +19,31 @@ import ( // CmdLoginHelper represents to login a gitea helper. var CmdLoginHelper = cli.Command{ - Name: "helper", - Aliases: []string{"git-credential"}, - Usage: "Git helper", - Description: `Git helper`, - Hidden: true, + Name: "helper", + Aliases: []string{"git-credential"}, + Usage: "Act as a git credential helper for stored Gitea logins", + Description: `Speaks git's credential helper protocol so that HTTPS push and clone +operations against your configured Gitea instances authenticate silently +using the tokens tea already stores. + +Typical use is automatic: 'tea login add --git-credentials' (or 'tea login +helper setup' for existing logins) registers '!tea login helper' as a +credential helper in ~/.gitconfig. Git then invokes the 'get' subcommand +when it needs credentials for a configured host.`, Commands: []*cli.Command{ { Name: "store", - Description: "Command drops", Aliases: []string{"erase"}, + Usage: "No-op (git credential protocol store/erase)", + Description: "No-op invoked by git on credential store/erase; tea persists credentials only in its own config", Action: func(requestCtx context.Context, _ *cli.Command) error { return nil }, }, { Name: "setup", - Description: "Setup helper to tea authenticate", + Usage: "Register tea as a git credential helper for every configured login", + Description: "Register tea as a git credential helper in ~/.gitconfig for every configured login", Action: func(requestCtx context.Context, _ *cli.Command) error { logins, err := config.GetLogins() if err != nil { @@ -56,7 +64,8 @@ var CmdLoginHelper = cli.Command{ }, { Name: "get", - Description: "Get token to auth", + Usage: "Return the stored token for a URL (git credential protocol)", + Description: "Return the stored token for a given URL in git's credential helper protocol (called by git, reads request from stdin)", Flags: []cli.Flag{ &cli.StringFlag{ Name: "login", diff --git a/docs/CLI.md b/docs/CLI.md index 07416b2e..d1e183c0 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -41,7 +41,7 @@ Add a Gitea login **--client-id**="": OAuth client ID (for use with --oauth) -**--helper, -j**: Add helper +**--git-credentials, --helper, -j**: Register tea as a git credential helper for this login's URL, so 'git push' and 'git clone' over HTTPS authenticate silently using the stored token **--insecure, -i**: Disable TLS verification @@ -87,6 +87,24 @@ Get or Set Default Login **--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json) +### helper, git-credential + +Act as a git credential helper for stored Gitea logins + +#### store, erase + +No-op (git credential protocol store/erase) + +#### setup + +Register tea as a git credential helper for every configured login + +#### get + +Return the stored token for a URL (git credential protocol) + +**--login, -l**="": Use a specific login + ### oauth-refresh Refresh an OAuth token diff --git a/modules/task/login_create.go b/modules/task/login_create.go index 1db94f8c..2a0e1050 100644 --- a/modules/task/login_create.go +++ b/modules/task/login_create.go @@ -146,6 +146,8 @@ func CreateLogin(ctx stdctx.Context, name, token, user, passwd, otp, scopes, ssh if _, err := SetupHelper(login); err != nil { return err } + } else { + fmt.Println("Tip: pass --git-credentials (or run 'tea login helper setup') to authenticate 'git push' and 'git clone' over HTTPS with this token.") } return nil