~emersion/hut-dev

todo ticket create: new command v1 PROPOSED

Simon Ser: 1
 todo ticket create: new command

 3 files changed, 104 insertions(+), 0 deletions(-)
Tickets having a wrong timestamp is a bug on our side after all [1]. I
think this needs a workaround in gqlclient until omitempty will work on
structs [2].

[1]: https://todo.sr.ht/~sircmpwn/todo.sr.ht/258
[2]: https://github.com/golang/go/issues/11939
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/hut-dev/patches/29580/mbox | git am -3
Learn more about email & git

[PATCH] todo ticket create: new command Export this patch

Pretty similar to `hut todo ticket comment`, except we need to
provide both a subject and a body. Add commented instructions when
using an editor, similar to git-commit's behavior.
---
 srht/todosrht/gql.go             | 11 ++++
 srht/todosrht/operations.graphql |  6 +++
 todo.go                          | 87 ++++++++++++++++++++++++++++++++
 3 files changed, 104 insertions(+)
diff --git a/srht/todosrht/gql.go b/srht/todosrht/gql.go
index e6cb2bfbdaa7..d1708fabe870 100644
--- a/srht/todosrht/gql.go
+++ b/srht/todosrht/gql.go
@@ -549,3 +549,14 @@ func UpdateTicketStatus(client *gqlclient.Client, ctx context.Context, trackerId
	err = client.Execute(ctx, op, &respData)
	return respData.UpdateTicketStatus, err
}

func SubmitTicket(client *gqlclient.Client, ctx context.Context, trackerId int32, input SubmitTicketInput) (submitTicket *Ticket, err error) {
	op := gqlclient.NewOperation("mutation submitTicket ($trackerId: Int!, $input: SubmitTicketInput!) {\n\tsubmitTicket(trackerId: $trackerId, input: $input) {\n\t\tid\n\t}\n}\n")
	op.Var("trackerId", trackerId)
	op.Var("input", input)
	var respData struct {
		SubmitTicket *Ticket
	}
	err = client.Execute(ctx, op, &respData)
	return respData.SubmitTicket, err
}
diff --git a/srht/todosrht/operations.graphql b/srht/todosrht/operations.graphql
index 883276f0ec8d..2d31a42d0331 100644
--- a/srht/todosrht/operations.graphql
+++ b/srht/todosrht/operations.graphql
@@ -98,3 +98,9 @@ mutation updateTicketStatus(
        }
    }
}

mutation submitTicket($trackerId: Int!, $input: SubmitTicketInput!) {
    submitTicket(trackerId: $trackerId, input: $input) {
        id
    }
}
diff --git a/todo.go b/todo.go
index 5860c3f739df..f4a060cf24b6 100644
--- a/todo.go
+++ b/todo.go
@@ -1,12 +1,14 @@
package main

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"unicode"

	"git.sr.ht/~emersion/hut/srht/todosrht"
	"git.sr.ht/~emersion/hut/termfmt"
@@ -110,6 +112,7 @@ func newTodoTicketCommand() *cobra.Command {
	cmd.AddCommand(newTodoTicketListCommand())
	cmd.AddCommand(newTodoTicketCommentCommand())
	cmd.AddCommand(newTodoTicketStatusCommand())
	cmd.AddCommand(newTodoTicketCreateCommand())
	return cmd
}

@@ -296,6 +299,90 @@ func newTodoTicketStatusCommand() *cobra.Command {
	return cmd
}

const todoTicketPrefill = `
<!--
Please enter the subject of the new ticket. The subject line can be
followed by a blank line and a Markdown description. An empty subject
aborts the ticket.
-->`

func newTodoTicketCreateCommand() *cobra.Command {
	var stdin bool
	run := func(cmd *cobra.Command, args []string) {
		ctx := cmd.Context()
		name, owner, instance := getTrackerName(ctx, cmd)
		c := createClientWithInstance("todo", cmd, instance)
		trackerID := getTrackerID(c, ctx, name, owner)

		var input todosrht.SubmitTicketInput
		if stdin {
			br := bufio.NewReader(os.Stdin)
			fmt.Printf("Subject: ")

			var err error
			input.Subject, err = br.ReadString('\n')
			if err != nil {
				log.Fatalf("failed to read subject: %v", err)
			}
			input.Subject = strings.TrimSpace(input.Subject)
			if input.Subject == "" {
				fmt.Println("Aborting due to empty subject.")
				os.Exit(1)
			}

			fmt.Printf("Description %s:\n", termfmt.Dim.String("(Markdown supported)"))
			bodyBytes, err := io.ReadAll(br)
			if err != nil {
				log.Fatalf("failed to read description: %v", err)
			}
			if body := strings.TrimSpace(string(bodyBytes)); body != "" {
				input.Body = &body
			}
		} else {
			text, err := getInputWithEditor("hut_ticket*.md", todoTicketPrefill)
			if err != nil {
				log.Fatalf("failed to read ticket subject and description: %v", err)
			}

			// Drop our prefilled comment, but without stripping leading
			// whitespace
			text = strings.TrimRightFunc(text, unicode.IsSpace)
			text = strings.TrimSuffix(text, todoTicketPrefill)
			text = strings.TrimRightFunc(text, unicode.IsSpace)

			parts := strings.SplitN(text, "\n", 2)
			input.Subject = strings.TrimSpace(parts[0])
			if len(parts) > 1 {
				body := strings.TrimSpace(parts[1])
				input.Body = &body
			}
		}

		if input.Subject == "" {
			fmt.Println("Aborting due to empty subject.")
			os.Exit(1)
		}

		ticket, err := todosrht.SubmitTicket(c.Client, ctx, trackerID, input)
		if err != nil {
			log.Fatal(err)
		} else if ticket == nil {
			log.Fatal("failed to create ticket")
		}

		fmt.Printf("Created new ticket %v\n", termfmt.DarkYellow.Sprintf("#%v", ticket.Id))
	}

	cmd := &cobra.Command{
		Use:   "create",
		Short: "Create a new ticket",
		Args:  cobra.ExactArgs(0),
		Run:   run,
	}
	cmd.Flags().BoolVar(&stdin, "stdin", false, "read ticket from stdin")
	return cmd
}

func getTrackerID(c *Client, ctx context.Context, name, owner string) int32 {
	var (
		tracker *todosrht.Tracker

base-commit: f7519c5d49a8287a0e47f9459cb859304d1e8b0c
-- 
2.35.1