mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
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>
This commit is contained in:
19 files changed
+2636
-646
No files matched your search
+50
-17
@@ -4,7 +4,9 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gitea.dev/tea/internal/services"
|
||||
"gitea.dev/tea/internal/terminal"
|
||||
@@ -13,25 +15,56 @@ import (
|
||||
|
||||
func (a *application) runJobsCommand() *cobra.Command {
|
||||
var limit, page int
|
||||
cmd := &cobra.Command{Use: "jobs <run-id>", Short: "List jobs belonging to a workflow run", Args: argsExact(1), RunE: func(cmd *cobra.Command, args []string) error {
|
||||
run, err := number(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target, err := a.resolve(cmd.Context(), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items, err := (services.Actions{Client: target.client, Repository: target.Repo}).JobsPage(cmd.Context(), run, limit, page)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeList(a, cmd, items, jobRow)
|
||||
}}
|
||||
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
|
||||
}
|
||||
|
||||
func jobRow(item services.Job) []terminal.Cell {
|
||||
return terminal.Row(strconv.FormatInt(item.ID, 10), item.Name, item.Status, item.Conclusion)
|
||||
// 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},
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user