mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
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:
1 parent
12e415a7a7
commit
f02245449b
456 files changed
+18892
-34015
No files matched your search
@@ -0,0 +1,142 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/tea/internal/settings"
|
||||
"gitea.dev/tea/internal/terminal"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// preferences applies environment overrides to the stored preferences, matching config get --show-source.
|
||||
func (a *application) preferences() (settings.Preferences, error) {
|
||||
snapshot, err := a.deps.Settings.Load()
|
||||
if err != nil {
|
||||
return settings.Preferences{}, err
|
||||
}
|
||||
preferences, _, err := a.effectivePreferences(snapshot)
|
||||
return preferences, err
|
||||
}
|
||||
|
||||
func (a *application) colorEnabled() bool {
|
||||
preferences, _ := a.preferences()
|
||||
return terminal.ColorEnabled(preferences.Color, a.deps.Streams.OutTTY, a.deps.Getenv)
|
||||
}
|
||||
|
||||
// writeHuman pages multi-line human list/view text. JSON callers must not use it.
|
||||
// --no-input does not disable paging.
|
||||
func (a *application) writeHuman(cmd *cobra.Command, text string) error {
|
||||
preferences, _ := a.preferences()
|
||||
return terminal.WriteHuman(cmd.Context(), a.deps.Streams, preferences.Pager, a.deps.Pager, text)
|
||||
}
|
||||
|
||||
// writeJSON writes v as JSON, filtered by --json fields and --jq.
|
||||
func (a *application) writeJSON(cmd *cobra.Command, v any) error {
|
||||
return terminal.WriteJSON(cmd.OutOrStdout(), v, a.options.json, a.options.jq)
|
||||
}
|
||||
|
||||
// abortBatch stops a batch instead of recording an item failure.
|
||||
type abortBatch struct{ error }
|
||||
|
||||
func (e abortBatch) Unwrap() error { return e.error }
|
||||
|
||||
// batch applies run to each selection and continues past individual failures, which
|
||||
// it reports prefixed with the selection's label. Human output is one table of the
|
||||
// results; JSON is an object for one selection (none if it failed), otherwise an array.
|
||||
func batch[K, T any](a *application, cmd *cobra.Command, keys []K, label func(K) string, run func(K) (T, error), row func(T) []terminal.Cell) error {
|
||||
results := []T{}
|
||||
var failures []error
|
||||
for _, key := range keys {
|
||||
if err := cmd.Context().Err(); err != nil {
|
||||
failures = append(failures, err)
|
||||
break
|
||||
}
|
||||
item, err := run(key)
|
||||
if errors.As(err, new(abortBatch)) {
|
||||
failures = append(failures, err)
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
failures = append(failures, fmt.Errorf("%s: %w", label(key), err))
|
||||
continue
|
||||
}
|
||||
results = append(results, item)
|
||||
}
|
||||
var err error
|
||||
switch {
|
||||
case a.options.json == "":
|
||||
err = a.writeHuman(cmd, table(results, row, a.colorEnabled()))
|
||||
case len(keys) != 1:
|
||||
err = a.writeJSON(cmd, results)
|
||||
case len(results) == 1:
|
||||
err = a.writeJSON(cmd, results[0])
|
||||
}
|
||||
return errors.Join(append(failures, err)...)
|
||||
}
|
||||
|
||||
// writeList writes items as a JSON array, or as one aligned table through the pager.
|
||||
func writeList[T any](a *application, cmd *cobra.Command, items []T, row func(T) []terminal.Cell) error {
|
||||
if a.options.json != "" {
|
||||
return a.writeJSON(cmd, items)
|
||||
}
|
||||
return a.writeHuman(cmd, table(items, row, a.colorEnabled()))
|
||||
}
|
||||
|
||||
// writeItem writes one item as a JSON object, or as its list row.
|
||||
func writeItem[T any](a *application, cmd *cobra.Command, item T, row func(T) []terminal.Cell) error {
|
||||
if a.options.json != "" {
|
||||
return a.writeJSON(cmd, item)
|
||||
}
|
||||
return a.writeHuman(cmd, table([]T{item}, row, a.colorEnabled()))
|
||||
}
|
||||
|
||||
// writeRecord writes one item as a JSON object, or as a human-readable record.
|
||||
func writeRecord[T any](a *application, cmd *cobra.Command, item T, record func(T) terminal.Record) error {
|
||||
if a.options.json != "" {
|
||||
return a.writeJSON(cmd, item)
|
||||
}
|
||||
return a.writeHuman(cmd, record(item).Render(a.colorEnabled()))
|
||||
}
|
||||
|
||||
// writeRecords writes items as a JSON array, or as consecutive human-readable records.
|
||||
func writeRecords[T any](a *application, cmd *cobra.Command, items []T, record func(T) terminal.Record) error {
|
||||
if a.options.json != "" {
|
||||
return a.writeJSON(cmd, items)
|
||||
}
|
||||
color := a.colorEnabled()
|
||||
var text strings.Builder
|
||||
for _, item := range items {
|
||||
text.WriteString(record(item).Render(color))
|
||||
}
|
||||
return a.writeHuman(cmd, text.String())
|
||||
}
|
||||
|
||||
// numberedRecord heads an issue or pull request with its emphasized number and title.
|
||||
func numberedRecord(number int64, title, url string) terminal.Record {
|
||||
return terminal.Record{Heading: []terminal.Cell{{Text: fmt.Sprintf("#%d", number), Bold: true}, {Text: title}}, Lines: []string{url}}
|
||||
}
|
||||
|
||||
// stateColors colors issue, pull request and milestone states on color terminals.
|
||||
var stateColors = map[string]string{"open": "2da44e", "closed": "cf222e", "merged": "8250df"}
|
||||
|
||||
func table[T any](items []T, row func(T) []terminal.Cell, color bool) string {
|
||||
rows := make([][]terminal.Cell, len(items))
|
||||
for i, item := range items {
|
||||
rows[i] = row(item)
|
||||
}
|
||||
return terminal.Table(rows, color)
|
||||
}
|
||||
|
||||
// morePagesHint tells human readers on stderr that the listing continues past this page.
|
||||
func (a *application) morePagesHint(cmd *cobra.Command, more bool, page int) error {
|
||||
if !more || a.options.json != "" {
|
||||
return nil
|
||||
}
|
||||
_, err := fmt.Fprintf(cmd.ErrOrStderr(), "more results available; use --page %d\n", page+1)
|
||||
return err
|
||||
}
|
||||
Reference in new issue
Block a user