fix(webhook): validate events and types, sort events and show delivery URLs

webhook create --events pushh created a webhook that fired on nothing,
since Gitea drops unknown events, and unknown types or a Slack webhook
without a channel failed with raw HTTP 422 errors. --events and --type
now accept only what Gitea knows, list it in help and complete it, and a
Slack webhook needs --config channel=.

Events came back in a different order on every call; they are sorted
now. Webhooks can be told apart again: list, view and JSON show the
delivery URL without user information and query, and without the path
for chat services whose paths carry tokens; view --reveal-url shows it
whole. Tables collapse complete event groups such as pull_request (8).

On a terminal, webhook edit no longer needs an authorization flag: since
Gitea drops the stored header on every edit and never returns it, it asks
for the header again and keeps it, and an empty answer suits webhooks
without one. Without a terminal the edit still requires the flags. Edit
flags lose the create defaults, the unsupported secret flags are hidden
on edit, deletions name the webhook and its scope, and mutations print ✓
lines. terminal.View gains Bodyless for resources without a body.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-11 23:42:49 +02:00
1 parent 769e93f5fd
commit ab3e91acd1
8 files changed
+666 -248

No files matched your search

+16 -9
View File
@@ -49,6 +49,8 @@ type View struct {
Meta string
Fields []Field
Body string
// Bodyless resources, such as webhooks, have no body: the view ends with the fields and URL.
Bodyless bool
// Kind names the resource in the footer, such as "issue".
Kind, URL string
}
@@ -68,6 +70,9 @@ func (v View) Render(d Display) string {
if v.URL != "" {
b.WriteString("url:\t" + SafeLine(v.URL) + "\n")
}
if v.Bodyless {
return b.String()
}
body := SafeText(v.Body)
b.WriteString("--\n" + body)
if !strings.HasSuffix(body, "\n") {
@@ -106,16 +111,18 @@ func (v View) Render(d Display) string {
b.WriteString(Cell{Bold: true}.style(label, d, false) + strings.Repeat(" ", labelWidth+2-runewidth.StringWidth(label)) + field.text(d) + "\n")
}
body := strings.TrimSpace(v.Body)
switch {
case body == "":
body = muted("No description provided")
case d.Markdown:
body = Markdown(body, d)
default:
body = SafeText(body)
if !v.Bodyless {
body := strings.TrimSpace(v.Body)
switch {
case body == "":
body = muted("No description provided")
case d.Markdown:
body = Markdown(body, d)
default:
body = SafeText(body)
}
b.WriteString("\n" + body + "\n")
}
b.WriteString("\n" + body + "\n")
if v.URL != "" {
kind := "View on Gitea: "
if v.Kind != "" {