Make issues create honor --output json

Same hole and same shape as the merged pulls fix (#1111):
--output parses on issues create through the urfave/cli v3
ancestor-flag cascade but was never read, so the action always
printed glamour markdown plus a second bare URL line.

task.CreateIssue now returns the created issue and the cmd layer
switches on --output, emitting compact lean JSON (index, title,
url, state), matching the post-review shape of
writeCreatedPullAsJSON. Without the flag the output stays
byte-identical: the same print.IssueDetails and HTMLURL println
calls just moved to the callers.

Signed-off-by: Danilo Sousa <code@danilosousa.net>
This commit is contained in:
Danilo Sousa 2026-09-09 20:57:31 -03:00
parent 4d09587d4c
commit 83fb7c0d26
No known key found for this signature in database
GPG key ID: E11D50EF201705CF
4 changed files with 101 additions and 14 deletions

View file

@ -5,13 +5,18 @@ package issues
import (
stdctx "context"
"encoding/json"
"fmt"
"io"
gitea "gitea.dev/sdk"
"github.com/urfave/cli/v3"
"gitea.dev/tea/cmd/flags"
"gitea.dev/tea/modules/context"
"gitea.dev/tea/modules/interact"
"gitea.dev/tea/modules/print"
"gitea.dev/tea/modules/task"
"github.com/urfave/cli/v3"
)
// 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 task.CreateIssue(requestCtx, ctx.Login,
issue, err := task.CreateIssue(requestCtx, ctx.Login,
ctx.Owner,
ctx.Repo,
*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,
})
}

41
cmd/issues/create_test.go Normal file
View file

@ -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")
}

View file

@ -5,11 +5,13 @@ package interact
import (
"context"
"fmt"
"strings"
gitea "gitea.dev/sdk"
"gitea.dev/tea/modules/config"
"gitea.dev/tea/modules/print"
"gitea.dev/tea/modules/task"
"gitea.dev/tea/modules/theme"
@ -34,7 +36,16 @@ func CreateIssue(ctx context.Context, login *config.Login, owner, repo string) e
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 {

View file

@ -10,24 +10,19 @@ import (
gitea "gitea.dev/sdk"
"gitea.dev/tea/modules/config"
"gitea.dev/tea/modules/print"
)
// CreateIssue creates an issue in the given repo and prints the result
func CreateIssue(requestCtx stdctx.Context, rlogin *config.Login, repoOwner, repoName string, opts gitea.CreateIssueOption) error {
// 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) (*gitea.Issue, error) {
// title is required
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)
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)
fmt.Println(issue.HTMLURL)
return nil
return issue, nil
}