// 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 ", 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 ", 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 + " ", 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) }