mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
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>
131 lines
4.6 KiB
Go
131 lines
4.6 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"gitea.dev/tea/internal/clierr"
|
|
"gitea.dev/tea/internal/services"
|
|
"gitea.dev/tea/internal/terminal"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func (a *application) workflowCommand() *cobra.Command {
|
|
group := &cobra.Command{Use: "workflow", Short: "Manage Actions workflows"}
|
|
group.AddCommand(&cobra.Command{Use: "list", Short: "List repository workflows", Args: argsExact(0), RunE: func(cmd *cobra.Command, _ []string) error {
|
|
t, err := a.resolve(cmd.Context(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
items, err := (services.Actions{Client: t.client, Repository: t.Repo}).Workflows(cmd.Context())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return writeList(a, cmd, items, workflowRow)
|
|
}})
|
|
group.AddCommand(&cobra.Command{Use: "view <id>", Short: "View a workflow", Args: argsExact(1), RunE: func(cmd *cobra.Command, args []string) error {
|
|
t, err := a.resolve(cmd.Context(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
item, err := (services.Actions{Client: t.client, Repository: t.Repo}).Workflow(cmd.Context(), args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return writeItem(a, cmd, item, workflowRow)
|
|
}})
|
|
var ref string
|
|
var fields, inputs []string
|
|
var follow bool
|
|
dispatch := &cobra.Command{Use: "run <id>", Short: "Dispatch a workflow", Args: argsExact(1), RunE: func(cmd *cobra.Command, args []string) error {
|
|
if follow && a.options.json != "" {
|
|
return clierr.Wrap(clierr.Usage, errors.New("--follow cannot be combined with --json/--jq"))
|
|
}
|
|
if ref == "" {
|
|
branch, err := a.currentBranch(cmd.Context())
|
|
if err != nil || branch == "" {
|
|
return clierr.Wrap(clierr.Usage, errors.New("--ref is required outside a branch checkout"))
|
|
}
|
|
ref = branch
|
|
}
|
|
values := map[string]string{}
|
|
for _, field := range append(fields, inputs...) {
|
|
pieces := strings.SplitN(field, "=", 2)
|
|
if len(pieces) != 2 || pieces[0] == "" {
|
|
return clierr.Wrap(clierr.Usage, errors.New("--field/--input requires key=value"))
|
|
}
|
|
if _, exists := values[pieces[0]]; exists {
|
|
return clierr.Wrap(clierr.Usage, errors.New("duplicate workflow input"))
|
|
}
|
|
values[pieces[0]] = pieces[1]
|
|
}
|
|
t, err := a.resolve(cmd.Context(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
service := services.Actions{Client: t.client, Repository: t.Repo}
|
|
result, err := service.Dispatch(cmd.Context(), args[0], ref, values)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if a.options.json != "" {
|
|
return a.writeJSON(cmd, result)
|
|
}
|
|
if result.ID == 0 {
|
|
if follow {
|
|
return errors.New("server did not return a run ID; cannot follow logs")
|
|
}
|
|
_, err := fmt.Fprintln(cmd.ErrOrStderr(), "Workflow dispatched; the server did not return a run ID")
|
|
return err
|
|
}
|
|
if _, err := fmt.Fprintf(cmd.OutOrStdout(), "%d\t%s\n", result.ID, result.URL); err != nil {
|
|
return err
|
|
}
|
|
if !follow {
|
|
return nil
|
|
}
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Waiting for run %d jobs...\n", result.ID)
|
|
ctx, cancel := context.WithTimeout(cmd.Context(), 30*time.Minute)
|
|
defer cancel()
|
|
job, err := service.WaitForFirstJob(ctx, result.ID, 2*time.Second)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Following job %d (%s)\n", job.ID, job.Name)
|
|
return service.FollowJobLogs(ctx, result.ID, job.ID, 2*time.Second, func(chunk []byte) error {
|
|
_, err := cmd.OutOrStdout().Write(chunk)
|
|
return err
|
|
})
|
|
}}
|
|
dispatch.Flags().StringVarP(&ref, "ref", "r", "", "Branch or tag to run (default: current branch)")
|
|
dispatch.Flags().StringArrayVarP(&fields, "field", "f", nil, "Workflow input key=value (repeatable)")
|
|
dispatch.Flags().StringArrayVarP(&inputs, "input", "i", nil, "Alias for --field")
|
|
dispatch.Flags().BoolVar(&follow, "follow", false, "Follow the first job's logs after dispatch")
|
|
group.AddCommand(dispatch)
|
|
for _, verb := range []string{"enable", "disable"} {
|
|
group.AddCommand(&cobra.Command{Use: verb + " <id>", Short: titleCase(verb) + " a workflow", Annotations: textOutput("workflow state changes do not return a resource"), Args: argsExact(1), RunE: func(cmd *cobra.Command, args []string) error {
|
|
t, err := a.resolve(cmd.Context(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if verb == "disable" {
|
|
if err := a.confirm(cmd, "Disable workflow "+args[0]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return (services.Actions{Client: t.client, Repository: t.Repo}).SetWorkflowEnabled(cmd.Context(), args[0], verb == "enable")
|
|
}})
|
|
}
|
|
return group
|
|
}
|
|
|
|
func workflowRow(item services.Workflow) []terminal.Cell {
|
|
return terminal.Row(item.ID, item.Name, item.State)
|
|
}
|