feat!: rebuild tea on Cobra with explicit noun/verb commands (#1126)

- **switch tea to the Cobra command tree:** `internal/app` wires the packages from parts 1–4 into explicit noun/verb commands with `--repo`, `--json` / `--jq`, `--sudo` and `--debug`; `main.go` points at it. Build, release and docs tooling (`tools/cli-docs`, Makefile, goreleaser, snap, CI) and README / CONTRIBUTING are updated.
- **remove the urfave/cli command tree:** deletes `cmd/`, `modules/`, the old integration tests, contrib completion scripts and the generated `docs/CLI.md`, and drops their dependencies. This commit only deletes.

## Breaking changes

This is a breaking release. It requires Gitea 28 or newer; configuration moves to separate `config.yml` and `hosts.yml` files and requires fresh authentication, and command names, aliases, flags and output formats change. `docs/migration-1.0.md` maps every old command, flag and environment variable to its replacement.

## Issues

fixes #1118
fixes #1116
fixes #1115
fixes #1094
fixes #1091
fixes #1073
fixes #1052
fixes #1032
fixes #1023
fixes #966
fixes #908
fixes #833
fixes #831
fixes #817
fixes #804
fixes #779
fixes #770
fixes #737
fixes #697
fixes #726
fixes #677
fixes #672
fixes #633
fixes #632
fixes #560
fixes #556
fixes #523
fixes #516
fixes #510
fixes #504
fixes #500
fixes #482
fixes #406
fixes #403
fixes #373
fixes #351
fixes #344
fixes #326
fixes #325
fixes #323
fixes #246
fixes #240
fixes #232
fixes #197
fixes #172
fixes #141
fixes #69
fixes #1110
fixes #569
fixes #480
fixes #666
fixes #670
fixes #457
fixes #643
fixes #498
fixes #490
fixes #1069
fixes #834
fixes #602
fixes #328
fixes #417
fixes #571
fixes #1029
fixes #489
fixes #1050
fixes #162
fixes #430
fixes #1087
fixes #683

Reviewed-on: https://gitea.com/gitea/tea/pulls/1126
Reviewed-by: TheFox0x7 <95654+thefox0x7@noreply.gitea.com>
This commit is contained in:
bircni committed 2026-10-11 17:23:06 +00:00
1 parent 12e415a7a7
commit f02245449b
456 files changed
+18892 -34015

No files matched your search

-331
View File
@@ -1,331 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package webhooks
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestListCommandMetadata(t *testing.T) {
cmd := &CmdWebhooksList
assert.Equal(t, "list", cmd.Name)
assert.Contains(t, cmd.Aliases, "ls")
assert.Equal(t, "List webhooks", cmd.Usage)
assert.Equal(t, "List webhooks in repository, organization, or globally", cmd.Description)
assert.NotNil(t, cmd.Action)
}
func TestListCommandFlags(t *testing.T) {
cmd := &CmdWebhooksList
// Should inherit from AllDefaultFlags which includes output, login, remote, repo flags
assert.NotNil(t, cmd.Flags)
assert.Greater(t, len(cmd.Flags), 0, "List command should have flags from AllDefaultFlags")
}
func TestListOutputFormats(t *testing.T) {
// Test that various output formats are supported through the output flag
supportedFormats := []string{
"table",
"csv",
"simple",
"tsv",
"yaml",
"json",
}
for _, format := range supportedFormats {
t.Run("Format_"+format, func(t *testing.T) {
// Verify format string is valid (non-empty, no spaces)
assert.NotEmpty(t, format)
assert.NotContains(t, format, " ")
})
}
}
func TestListPagination(t *testing.T) {
// Test pagination parameters that would be used with ListHooksOptions
tests := []struct {
name string
page int
pageSize int
valid bool
}{
{
name: "Default pagination",
page: 1,
pageSize: 10,
valid: true,
},
{
name: "Large page size",
page: 1,
pageSize: 100,
valid: true,
},
{
name: "High page number",
page: 50,
pageSize: 10,
valid: true,
},
{
name: "Zero page",
page: 0,
pageSize: 10,
valid: false,
},
{
name: "Negative page",
page: -1,
pageSize: 10,
valid: false,
},
{
name: "Zero page size",
page: 1,
pageSize: 0,
valid: false,
},
{
name: "Negative page size",
page: 1,
pageSize: -10,
valid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.valid {
assert.Greater(t, tt.page, 0, "Valid page should be positive")
assert.Greater(t, tt.pageSize, 0, "Valid page size should be positive")
} else {
assert.True(t, tt.page <= 0 || tt.pageSize <= 0, "Invalid pagination should have non-positive values")
}
})
}
}
func TestListSorting(t *testing.T) {
// Test potential sorting options for webhook lists
sortFields := []string{
"id",
"type",
"url",
"active",
"created",
"updated",
}
for _, field := range sortFields {
t.Run("SortField_"+field, func(t *testing.T) {
assert.NotEmpty(t, field)
assert.NotContains(t, field, " ")
})
}
}
func TestListFiltering(t *testing.T) {
// Test filtering criteria that might be applied to webhook lists
tests := []struct {
name string
filterType string
filterValue string
valid bool
}{
{
name: "Filter by type - gitea",
filterType: "type",
filterValue: "gitea",
valid: true,
},
{
name: "Filter by type - slack",
filterType: "type",
filterValue: "slack",
valid: true,
},
{
name: "Filter by active status",
filterType: "active",
filterValue: "true",
valid: true,
},
{
name: "Filter by inactive status",
filterType: "active",
filterValue: "false",
valid: true,
},
{
name: "Filter by event",
filterType: "event",
filterValue: "push",
valid: true,
},
{
name: "Invalid filter type",
filterType: "invalid",
filterValue: "value",
valid: false,
},
{
name: "Empty filter value",
filterType: "type",
filterValue: "",
valid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.valid {
assert.NotEmpty(t, tt.filterType)
assert.NotEmpty(t, tt.filterValue)
} else {
assert.True(t, tt.filterType == "invalid" || tt.filterValue == "")
}
})
}
}
func TestListCommandStructure(t *testing.T) {
cmd := &CmdWebhooksList
// Verify command structure
assert.NotEmpty(t, cmd.Name)
assert.NotEmpty(t, cmd.Usage)
assert.NotEmpty(t, cmd.Description)
assert.NotNil(t, cmd.Action)
// Verify aliases
assert.Greater(t, len(cmd.Aliases), 0, "List command should have aliases")
for _, alias := range cmd.Aliases {
assert.NotEmpty(t, alias)
assert.NotContains(t, alias, " ")
}
}
func TestListErrorHandling(t *testing.T) {
// Test various error conditions that the list command should handle
errorCases := []struct {
name string
description string
}{
{
name: "Network error",
description: "Should handle network connectivity issues",
},
{
name: "Authentication error",
description: "Should handle authentication failures",
},
{
name: "Permission error",
description: "Should handle insufficient permissions",
},
{
name: "Repository not found",
description: "Should handle missing repository",
},
{
name: "Invalid output format",
description: "Should handle unsupported output formats",
},
}
for _, errorCase := range errorCases {
t.Run(errorCase.name, func(t *testing.T) {
// Verify error case is documented
assert.NotEmpty(t, errorCase.description)
})
}
}
func TestListTableHeaders(t *testing.T) {
// Test expected table headers for webhook list output
expectedHeaders := []string{
"ID",
"Type",
"URL",
"Events",
"Active",
"Updated",
}
for _, header := range expectedHeaders {
t.Run("Header_"+header, func(t *testing.T) {
assert.NotEmpty(t, header)
assert.NotContains(t, header, "\n")
})
}
// Verify all headers are unique
headerSet := make(map[string]bool)
for _, header := range expectedHeaders {
assert.False(t, headerSet[header], "Header %s appears multiple times", header)
headerSet[header] = true
}
}
func TestListEventFormatting(t *testing.T) {
// Test event list formatting for display
tests := []struct {
name string
events []string
maxLength int
expectedFormat string
}{
{
name: "Short event list",
events: []string{"push"},
maxLength: 40,
expectedFormat: "push",
},
{
name: "Multiple events",
events: []string{"push", "pull_request"},
maxLength: 40,
expectedFormat: "push,pull_request",
},
{
name: "Long event list - should truncate",
events: []string{"push", "pull_request", "pull_request_review_approved", "pull_request_sync"},
maxLength: 40,
expectedFormat: "truncated",
},
{
name: "Empty events",
events: []string{},
maxLength: 40,
expectedFormat: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
eventStr := ""
if len(tt.events) > 0 {
eventStr = tt.events[0]
for i := 1; i < len(tt.events); i++ {
eventStr += "," + tt.events[i]
}
}
if len(eventStr) > tt.maxLength && tt.maxLength > 3 {
eventStr = eventStr[:tt.maxLength-3] + "..."
}
if tt.expectedFormat == "truncated" {
assert.Contains(t, eventStr, "...")
} else if tt.expectedFormat != "" {
assert.Equal(t, tt.expectedFormat, eventStr)
}
})
}
}