mirror of
https://gitea.com/gitea/tea.git
synced 2026-09-15 11:28:11 +02:00
Make tea issues create honor --output json (#1114)
## Problem Follow-up to #1111, covering the issues side of the same hole: `tea issues create` accepts `--output` (it parses through the urfave/cli v3 ancestor-flag cascade — `issues` carries the flag via `AllDefaultFlags`, `create` never declares it) but the action never reads it. Without this fix, `tea issues create --output json | jq .url` feeds jq a markdown document. The default output is doubly hostile to consumers: glamour renders the details as markdown (with OSC 8 hyperlinks around the URL when piped), and a second bare `fmt.Println(issue.HTMLURL)` line follows it. ## What this changes - `task.CreateIssue` now returns the created `*gitea.Issue` instead of printing it. - `runIssuesCreate` switches on `--output`, mirroring the detail-command precedent and the merged create-PR behavior from #1111: `--output json` emits compact lean JSON; any other value (or no flag) falls through to the previous rendering, byte-identical to before. - Lean JSON shape: `index`, `title`, `url`, `state` — matching `createdPullJSON` in `cmd/pulls/create.go`, including its post-review compact encoding. - The interactive path is untouched — it only triggers when zero flags are set, so `--output` can never be active there. Example: ``` $ tea issues create --output json --title "bug: thing" | jq -r .url https://gitea.example.com/owner/repo/issues/42 ``` There is no agit-flow equivalent on issues, so no extra guard is needed — unlike the pulls side, every creation path produces an `*gitea.Issue`. --------- Co-authored-by: Danilo Sousa <code@danilosousa.net> Reviewed-on: https://gitea.com/gitea/tea/pulls/1114 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: ongolk <238961+ongolk@noreply.gitea.com>
This commit is contained in:
committed by
Lunny Xiao
co-authored by
Danilo Sousa
parent
4d09587d4c
commit
9c12138d62
+43
-3
@@ -5,13 +5,18 @@ package issues
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
stdctx "context"
|
stdctx "context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
gitea "gitea.dev/sdk"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
|
||||||
"gitea.dev/tea/cmd/flags"
|
"gitea.dev/tea/cmd/flags"
|
||||||
"gitea.dev/tea/modules/context"
|
"gitea.dev/tea/modules/context"
|
||||||
"gitea.dev/tea/modules/interact"
|
"gitea.dev/tea/modules/interact"
|
||||||
|
"gitea.dev/tea/modules/print"
|
||||||
"gitea.dev/tea/modules/task"
|
"gitea.dev/tea/modules/task"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdIssuesCreate represents a sub command of issues to create issue
|
// CmdIssuesCreate represents a sub command of issues to create issue
|
||||||
@@ -47,9 +52,44 @@ func runIssuesCreate(requestCtx stdctx.Context, cmd *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return task.CreateIssue(requestCtx, ctx.Login,
|
issue, err := task.CreateIssue(requestCtx, ctx.Login,
|
||||||
ctx.Owner,
|
ctx.Owner,
|
||||||
ctx.Repo,
|
ctx.Repo,
|
||||||
*opts,
|
*opts,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet("output") {
|
||||||
|
switch ctx.String("output") {
|
||||||
|
case "json":
|
||||||
|
return writeCreatedIssueAsJSON(ctx.Writer, issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print.IssueDetails(issue, nil)
|
||||||
|
|
||||||
|
fmt.Println(issue.HTMLURL)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createdIssueJSON is the machine-readable representation of a freshly
|
||||||
|
// created issue, mirroring the create-PR equivalent in cmd/pulls/create.go
|
||||||
|
// (createdPullJSON).
|
||||||
|
type createdIssueJSON struct {
|
||||||
|
Index int64 `json:"index"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
State gitea.StateType `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeCreatedIssueAsJSON(w io.Writer, issue *gitea.Issue) error {
|
||||||
|
return json.NewEncoder(w).Encode(createdIssueJSON{
|
||||||
|
Index: issue.Index,
|
||||||
|
Title: issue.Title,
|
||||||
|
URL: issue.HTMLURL,
|
||||||
|
State: issue.State,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package issues
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
gitea "gitea.dev/sdk"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWriteCreatedIssueAsJSON(t *testing.T) {
|
||||||
|
issue := &gitea.Issue{
|
||||||
|
Index: 42,
|
||||||
|
Title: "test title",
|
||||||
|
HTMLURL: "https://gitea.example.com/owner/repo/issues/42",
|
||||||
|
State: gitea.StateOpen,
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
require.NoError(t, writeCreatedIssueAsJSON(&buf, issue))
|
||||||
|
|
||||||
|
var got map[string]any
|
||||||
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &got))
|
||||||
|
|
||||||
|
assert.Equal(t, float64(42), got["index"])
|
||||||
|
assert.Equal(t, "test title", got["title"])
|
||||||
|
assert.Equal(t, "https://gitea.example.com/owner/repo/issues/42", got["url"])
|
||||||
|
assert.Equal(t, "open", got["state"])
|
||||||
|
|
||||||
|
// exactly the lean field set, nothing extra
|
||||||
|
assert.Len(t, got, 4)
|
||||||
|
|
||||||
|
// machine-readable output must not contain terminal escape sequences
|
||||||
|
assert.NotContains(t, buf.String(), "\x1b")
|
||||||
|
}
|
||||||
@@ -5,11 +5,13 @@ package interact
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
gitea "gitea.dev/sdk"
|
gitea "gitea.dev/sdk"
|
||||||
|
|
||||||
"gitea.dev/tea/modules/config"
|
"gitea.dev/tea/modules/config"
|
||||||
|
"gitea.dev/tea/modules/print"
|
||||||
"gitea.dev/tea/modules/task"
|
"gitea.dev/tea/modules/task"
|
||||||
"gitea.dev/tea/modules/theme"
|
"gitea.dev/tea/modules/theme"
|
||||||
|
|
||||||
@@ -34,7 +36,16 @@ func CreateIssue(ctx context.Context, login *config.Login, owner, repo string) e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return task.CreateIssue(ctx, login, owner, repo, opts)
|
issue, err := task.CreateIssue(ctx, login, owner, repo, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
print.IssueDetails(issue, nil)
|
||||||
|
|
||||||
|
fmt.Println(issue.HTMLURL)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func promptIssueProperties(ctx context.Context, login *config.Login, owner, repo string, o *gitea.CreateIssueOption) error {
|
func promptIssueProperties(ctx context.Context, login *config.Login, owner, repo string, o *gitea.CreateIssueOption) error {
|
||||||
|
|||||||
@@ -10,24 +10,19 @@ import (
|
|||||||
gitea "gitea.dev/sdk"
|
gitea "gitea.dev/sdk"
|
||||||
|
|
||||||
"gitea.dev/tea/modules/config"
|
"gitea.dev/tea/modules/config"
|
||||||
"gitea.dev/tea/modules/print"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateIssue creates an issue in the given repo and prints the result
|
// CreateIssue creates an issue in the given repo and returns the created issue
|
||||||
func CreateIssue(requestCtx stdctx.Context, rlogin *config.Login, repoOwner, repoName string, opts gitea.CreateIssueOption) error {
|
func CreateIssue(requestCtx stdctx.Context, rlogin *config.Login, repoOwner, repoName string, opts gitea.CreateIssueOption) (*gitea.Issue, error) {
|
||||||
// title is required
|
// title is required
|
||||||
if len(opts.Title) == 0 {
|
if len(opts.Title) == 0 {
|
||||||
return fmt.Errorf("title is required")
|
return nil, fmt.Errorf("title is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
issue, _, err := rlogin.Client().Issues.CreateIssue(requestCtx, repoOwner, repoName, opts)
|
issue, _, err := rlogin.Client().Issues.CreateIssue(requestCtx, repoOwner, repoName, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create issue: %s", err)
|
return nil, fmt.Errorf("could not create issue: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
print.IssueDetails(issue, nil)
|
return issue, nil
|
||||||
|
|
||||||
fmt.Println(issue.HTMLURL)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user