feat: explain failed requests and exit silently on interrupt

Failures of tea's own API requests now name what was missing or refused
instead of "resource or API endpoint unavailable":

  issue #999 not found in alice/project
  repository alice/nope not found on http://localhost:3001 (or you don't have access)
  issues are disabled for bob/project
  #1 in alice/project is an issue, not a pull request; view it with: tea issue view 1
  alice/old-stuff is archived (read-only); unarchive it with: tea repo edit ...

A 404 on a repository endpoint looks the repository up once to tell a missing
or hidden repository from a disabled feature, an empty repository or an issue
number given to a pull request command. Refusals get their next step, a
rejected GITEA_TOKEN and unreachable servers are named, and timeoutError words
an expired --timeout. Errors a command already wrapped with its own context,
and raw tea api responses, keep their wording.

Ctrl-C and SIGTERM exit with 130 without printing an error, and a declined
confirmation prints "Canceled." with exit code 3.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-11 22:48:49 +02:00
1 parent 2d4d34ddfc
commit 9b1d3a61e6
4 files changed
+437 -9

No files matched your search

+19 -7
View File
@@ -18,14 +18,26 @@ import (
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
deps := app.System()
err := app.Execute(ctx, deps, os.Args[1:])
if ctx.Err() != nil && errors.Is(err, context.Canceled) {
code := run(ctx, app.System(), os.Args[1:])
stop()
os.Exit(code)
}
// run executes tea, reports a failure on stderr and returns the exit code. An interrupt exits
// silently, and a declined confirmation prints only "Canceled.".
func run(ctx context.Context, deps app.Dependencies, args []string) int {
err := app.Execute(ctx, deps, args)
if err != nil && ctx.Err() != nil {
// Whatever failed after Ctrl-C or SIGTERM failed because of it; the user knows why.
err = clierr.Wrap(clierr.Interrupted, err)
}
if err != nil {
fmt.Fprintf(deps.Streams.ErrOut, "Error: %v\n", err)
code := clierr.ExitCode(err)
switch {
case err == nil, code == clierr.Interrupted:
case code == clierr.Canceled && !errors.Is(err, context.DeadlineExceeded):
fmt.Fprintln(deps.Streams.ErrOut, "Canceled.")
default:
fmt.Fprintf(deps.Streams.ErrOut, "Error: %v\n", app.Explain(ctx, deps, err))
}
os.Exit(clierr.ExitCode(err))
return code
}