Files
gitea-tea/internal/app/action_jobs.go
T
bircni 9f69a89537 feat(run): show runs like gh, watch them live, and download artifacts
run list prints a table with a status icon, the workflow name, title,
branch, event, elapsed time and age, and the total from the server; runs
carry workflow, workflow_path and run_number in JSON. run view shows the
run's state and trigger, its jobs with their steps and durations, the
next steps and the run's URL; --json includes the jobs, --log prints
their logs and --web opens the run.

Run commands accept #N and run URLs, and ask for a recent run in a
terminal when the argument is left out. run watch redraws the run on the
alternate screen, then prints it with a summary; an expired --timeout
names the run and suspects a missing runner when no job started.

run cancel no longer asks for confirmation, rerun refuses unfinished runs
up front, reruns a single job with --job and reports the new attempt,
and delete names the run in its prompt. run logs follows several jobs
with --follow, prefixing lines with the job's name, selects jobs by name
or with --failed, and downloads the archive with --output or --dir.
run download extracts the artifacts of a run by name, and artifact list
shows sizes and expiry.

Signed-off-by: bircni <bircni@icloud.com>
2026-10-11 23:46:26 +02:00

71 lines
2.1 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package app
import (
"fmt"
"strconv"
"time"
"gitea.dev/tea/internal/services"
"gitea.dev/tea/internal/terminal"
"github.com/spf13/cobra"
)
func (a *application) runJobsCommand() *cobra.Command {
var limit, page int
cmd := &cobra.Command{
Use: "jobs [<run-id>]",
Short: "List the jobs of a workflow run",
Example: `# list the jobs of a run
$ tea run jobs 34
# list the IDs of the jobs that failed
$ tea run jobs 34 --json id,conclusion --jq '.[] | select(.conclusion == "failure") | .id'`,
Args: usageArgs(cobra.MaximumNArgs(1)),
Annotations: map[string]string{"tea.arguments": runArguments},
RunE: func(cmd *cobra.Command, args []string) error {
t, run, err := a.runArg(cmd, args, false)
if err != nil {
return err
}
jobs, err := (services.Actions{Client: t.client, Repository: t.Repo}).JobsPage(cmd.Context(), run, limit, page)
if err != nil {
return err
}
spec := listSpec[services.Job]{Columns: jobColumns, Row: jobRow(a.deps.Now()), Empty: fmt.Sprintf("no jobs in run %d", run), Context: fmt.Sprintf("jobs in run %d", run)}
return writeTable(a, cmd, jobs.Items, spec, listPage{Number: page, More: jobs.More, Total: jobs.Total, Counted: jobs.Counted})
},
}
pageFlags(cmd, &limit, &page)
cmd.ValidArgsFunction = a.completeRunArg()
return cmd
}
// jobColumns head jobRow: an icon on a terminal, the status and conclusion words for scripts.
var jobColumns = []terminal.Column{
{Header: "Status", TTYOnly: true},
{Header: "Status", PipedOnly: true},
{Header: "Conclusion", PipedOnly: true},
{Header: "ID"},
{Header: "Name", Flex: true},
{Header: "Elapsed"},
{Header: "URL", PipedOnly: true},
}
func jobRow(now time.Time) func(services.Job) []terminal.Cell {
return func(job services.Job) []terminal.Cell {
icon := terminal.StatusIcon(job.Status, job.Conclusion)
icon.Link = job.URL
return []terminal.Cell{
icon,
{Text: job.Status},
{Text: job.Conclusion},
{Text: strconv.FormatInt(job.ID, 10)},
{Text: job.Name},
{Text: runElapsedText(job.StartedAt, job.CompletedAt, now)},
{Text: job.URL},
}
}
}