feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output, top-level run/workflow aliases matching gh CLI structure. Enhance actions, issues, PRs, releases, repos, labels, milestones, and wiki commands with improved flags, JSON output, and error handling.
This commit is contained in:
parent
7c0dcc8696
commit
113505de95
29 changed files with 3131 additions and 542 deletions
393
cmd/actions.go
393
cmd/actions.go
|
|
@ -3,10 +3,8 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
|
|
@ -100,39 +98,67 @@ var runListCmd = &cobra.Command{
|
|||
Use: "list",
|
||||
Short: "List recent workflow runs",
|
||||
Long: "List recent workflow runs for a repository.",
|
||||
RunE: runRunList,
|
||||
Example: ` # List recent workflow runs
|
||||
fgj actions run list
|
||||
|
||||
# List runs with a custom limit
|
||||
fgj actions run list -L 50
|
||||
|
||||
# Output as JSON
|
||||
fgj actions run list --json`,
|
||||
RunE: runRunList,
|
||||
}
|
||||
|
||||
var runViewCmd = &cobra.Command{
|
||||
Use: "view <run-id>",
|
||||
Short: "View a workflow run",
|
||||
Long: "View details about a specific workflow run.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRunView,
|
||||
Example: ` # View a workflow run
|
||||
fgj actions run view 123
|
||||
|
||||
# View with job details
|
||||
fgj actions run view 123 -v
|
||||
|
||||
# View logs for a specific job
|
||||
fgj actions run view 123 --job 456 --log
|
||||
|
||||
# View only failed logs
|
||||
fgj actions run view 123 --log-failed`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRunView,
|
||||
}
|
||||
|
||||
var runWatchCmd = &cobra.Command{
|
||||
Use: "watch <run-id>",
|
||||
Short: "Watch a workflow run",
|
||||
Long: "Poll a workflow run until it completes.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRunWatch,
|
||||
Example: ` # Watch a run until it completes
|
||||
fgj actions run watch 123
|
||||
|
||||
# Watch with a custom polling interval
|
||||
fgj actions run watch 123 -i 10s`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRunWatch,
|
||||
}
|
||||
|
||||
var runRerunCmd = &cobra.Command{
|
||||
Use: "rerun <run-id>",
|
||||
Short: "Rerun a workflow run",
|
||||
Long: "Trigger a rerun for a specific workflow run.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRunRerun,
|
||||
Example: ` # Rerun a failed workflow run
|
||||
fgj actions run rerun 123`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRunRerun,
|
||||
}
|
||||
|
||||
var runCancelCmd = &cobra.Command{
|
||||
Use: "cancel <run-id>",
|
||||
Short: "Cancel a workflow run",
|
||||
Long: "Cancel a running workflow run.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRunCancel,
|
||||
Example: ` # Cancel a running workflow
|
||||
fgj actions run cancel 123`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runRunCancel,
|
||||
}
|
||||
|
||||
// Workflow commands
|
||||
|
|
@ -146,39 +172,61 @@ var workflowListCmd = &cobra.Command{
|
|||
Use: "list",
|
||||
Short: "List workflows",
|
||||
Long: "List all workflows in a repository.",
|
||||
RunE: runWorkflowList,
|
||||
Example: ` # List all workflows
|
||||
fgj actions workflow list
|
||||
|
||||
# List workflows as JSON
|
||||
fgj actions workflow list --json
|
||||
|
||||
# List workflows for a specific repo
|
||||
fgj actions workflow list -R owner/repo`,
|
||||
RunE: runWorkflowList,
|
||||
}
|
||||
|
||||
var workflowViewCmd = &cobra.Command{
|
||||
Use: "view <workflow>",
|
||||
Short: "View a workflow",
|
||||
Long: "View details about a specific workflow. You can specify the workflow by name or filename.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runWorkflowView,
|
||||
Example: ` # View a workflow by filename
|
||||
fgj actions workflow view ci.yml
|
||||
|
||||
# View as JSON
|
||||
fgj actions workflow view ci.yml --json`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runWorkflowView,
|
||||
}
|
||||
|
||||
var workflowRunCmd = &cobra.Command{
|
||||
Use: "run <workflow>",
|
||||
Short: "Run a workflow",
|
||||
Long: "Trigger a workflow_dispatch event for a workflow. The workflow must support the workflow_dispatch trigger.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runWorkflowRun,
|
||||
Example: ` # Trigger a workflow on the default branch
|
||||
fgj actions workflow run deploy.yml
|
||||
|
||||
# Trigger on a specific branch with input parameters
|
||||
fgj actions workflow run deploy.yml -r staging -f environment=staging -f version=1.2.3`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runWorkflowRun,
|
||||
}
|
||||
|
||||
var workflowEnableCmd = &cobra.Command{
|
||||
Use: "enable <workflow>",
|
||||
Short: "Enable a workflow",
|
||||
Long: "Enable a workflow so it can be triggered.\n\nNote: This feature requires Forgejo 15.0+ or Gitea 1.24+.\nFor older versions, use the web UI to enable workflows.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runWorkflowEnable,
|
||||
Example: ` # Enable a workflow
|
||||
fgj actions workflow enable ci.yml`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runWorkflowEnable,
|
||||
}
|
||||
|
||||
var workflowDisableCmd = &cobra.Command{
|
||||
Use: "disable <workflow>",
|
||||
Short: "Disable a workflow",
|
||||
Long: "Disable a workflow so it cannot be triggered.\n\nNote: This feature requires Forgejo 15.0+ or Gitea 1.24+.\nFor older versions, use the web UI to disable workflows.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runWorkflowDisable,
|
||||
Example: ` # Disable a workflow
|
||||
fgj actions workflow disable ci.yml`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runWorkflowDisable,
|
||||
}
|
||||
|
||||
// Secret commands
|
||||
|
|
@ -192,23 +240,35 @@ var actionsSecretListCmd = &cobra.Command{
|
|||
Use: "list",
|
||||
Short: "List repository secrets",
|
||||
Long: "List all secrets for a repository.",
|
||||
RunE: runActionsSecretList,
|
||||
Example: ` # List all secrets
|
||||
fgj actions secret list
|
||||
|
||||
# List secrets for a specific repo
|
||||
fgj actions secret list -R owner/repo`,
|
||||
RunE: runActionsSecretList,
|
||||
}
|
||||
|
||||
var actionsSecretCreateCmd = &cobra.Command{
|
||||
Use: "create <name>",
|
||||
Short: "Create or update a repository secret",
|
||||
Long: "Create or update a secret for Forgejo Actions. The secret value will be read from stdin.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runActionsSecretCreate,
|
||||
Example: ` # Create a secret (will prompt for value)
|
||||
fgj actions secret create DEPLOY_TOKEN
|
||||
|
||||
# Create a secret for a specific repo
|
||||
fgj actions secret create API_KEY -R owner/repo`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runActionsSecretCreate,
|
||||
}
|
||||
|
||||
var actionsSecretDeleteCmd = &cobra.Command{
|
||||
Use: "delete <name>",
|
||||
Short: "Delete a repository secret",
|
||||
Long: "Delete a secret from Forgejo Actions.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runActionsSecretDelete,
|
||||
Example: ` # Delete a secret
|
||||
fgj actions secret delete DEPLOY_TOKEN`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runActionsSecretDelete,
|
||||
}
|
||||
|
||||
// Variable commands
|
||||
|
|
@ -222,39 +282,55 @@ var actionsVariableListCmd = &cobra.Command{
|
|||
Use: "list",
|
||||
Short: "List repository variables",
|
||||
Long: "List all variables for a repository.",
|
||||
RunE: runActionsVariableList,
|
||||
Example: ` # List all variables
|
||||
fgj actions variable list
|
||||
|
||||
# List variables for a specific repo
|
||||
fgj actions variable list -R owner/repo`,
|
||||
RunE: runActionsVariableList,
|
||||
}
|
||||
|
||||
var actionsVariableGetCmd = &cobra.Command{
|
||||
Use: "get <name>",
|
||||
Short: "Get a repository variable",
|
||||
Long: "Get the value of a specific repository variable.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runActionsVariableGet,
|
||||
Example: ` # Get a variable value
|
||||
fgj actions variable get ENVIRONMENT`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runActionsVariableGet,
|
||||
}
|
||||
|
||||
var actionsVariableCreateCmd = &cobra.Command{
|
||||
Use: "create <name> <value>",
|
||||
Short: "Create a repository variable",
|
||||
Long: "Create a new variable for Forgejo Actions.",
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: runActionsVariableCreate,
|
||||
Example: ` # Create a variable
|
||||
fgj actions variable create ENVIRONMENT production
|
||||
|
||||
# Create a variable for a specific repo
|
||||
fgj actions variable create NODE_VERSION 20 -R owner/repo`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: runActionsVariableCreate,
|
||||
}
|
||||
|
||||
var actionsVariableUpdateCmd = &cobra.Command{
|
||||
Use: "update <name> <value>",
|
||||
Short: "Update a repository variable",
|
||||
Long: "Update an existing variable for Forgejo Actions.",
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: runActionsVariableUpdate,
|
||||
Example: ` # Update a variable
|
||||
fgj actions variable update ENVIRONMENT staging`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: runActionsVariableUpdate,
|
||||
}
|
||||
|
||||
var actionsVariableDeleteCmd = &cobra.Command{
|
||||
Use: "delete <name>",
|
||||
Short: "Delete a repository variable",
|
||||
Long: "Delete a variable from Forgejo Actions.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runActionsVariableDelete,
|
||||
Example: ` # Delete a variable
|
||||
fgj actions variable delete ENVIRONMENT`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runActionsVariableDelete,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -293,13 +369,13 @@ func init() {
|
|||
// Add flags for run commands
|
||||
addRepoFlags(runListCmd)
|
||||
runListCmd.Flags().IntP("limit", "L", 20, "Maximum number of runs to list")
|
||||
runListCmd.Flags().Bool("json", false, "Output workflow runs as JSON")
|
||||
addJSONFlags(runListCmd, "Output workflow runs as JSON")
|
||||
addRepoFlags(runViewCmd)
|
||||
runViewCmd.Flags().BoolP("verbose", "v", false, "Show job steps")
|
||||
runViewCmd.Flags().BoolP("log", "", false, "View full log for either a run or specific job")
|
||||
runViewCmd.Flags().StringP("job", "j", "", "View a specific job ID from a run")
|
||||
runViewCmd.Flags().BoolP("log-failed", "", false, "View the log for any failed steps in a run or specific job")
|
||||
runViewCmd.Flags().Bool("json", false, "Output workflow run as JSON")
|
||||
addJSONFlags(runViewCmd, "Output workflow run as JSON")
|
||||
addRepoFlags(runWatchCmd)
|
||||
runWatchCmd.Flags().DurationP("interval", "i", 5*time.Second, "Polling interval")
|
||||
addRepoFlags(runRerunCmd)
|
||||
|
|
@ -308,9 +384,9 @@ func init() {
|
|||
// Add flags for workflow commands
|
||||
addRepoFlags(workflowListCmd)
|
||||
workflowListCmd.Flags().IntP("limit", "L", 20, "Maximum number of workflows to list")
|
||||
workflowListCmd.Flags().Bool("json", false, "Output workflows as JSON")
|
||||
addJSONFlags(workflowListCmd, "Output workflows as JSON")
|
||||
addRepoFlags(workflowViewCmd)
|
||||
workflowViewCmd.Flags().Bool("json", false, "Output workflow as JSON")
|
||||
addJSONFlags(workflowViewCmd, "Output workflow as JSON")
|
||||
addRepoFlags(workflowRunCmd)
|
||||
addRepoFlags(workflowEnableCmd)
|
||||
addRepoFlags(workflowDisableCmd)
|
||||
|
|
@ -325,6 +401,7 @@ func init() {
|
|||
|
||||
// Add flags for variable commands
|
||||
addRepoFlags(actionsVariableListCmd)
|
||||
addJSONFlags(actionsVariableListCmd, "Output variables as JSON")
|
||||
addRepoFlags(actionsVariableGetCmd)
|
||||
addRepoFlags(actionsVariableCreateCmd)
|
||||
addRepoFlags(actionsVariableUpdateCmd)
|
||||
|
|
@ -364,39 +441,26 @@ func runRunList(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to list runs: %w", err)
|
||||
}
|
||||
|
||||
if jsonOutput, _ := cmd.Flags().GetBool("json"); jsonOutput {
|
||||
return writeJSON(runList.WorkflowRuns)
|
||||
if wantJSON(cmd) {
|
||||
return outputJSON(cmd, runList.WorkflowRuns)
|
||||
}
|
||||
|
||||
if len(runList.WorkflowRuns) == 0 {
|
||||
fmt.Println("No workflow runs found")
|
||||
fmt.Fprintln(ios.Out, "No workflow runs found")
|
||||
return nil
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
if _, err := fmt.Fprintln(w, "STATUS\tTITLE\tWORKFLOW\tEVENT\tID\tCREATED"); err != nil {
|
||||
return fmt.Errorf("failed to write header: %w", err)
|
||||
}
|
||||
|
||||
tp := ios.NewTablePrinter()
|
||||
tp.AddHeader("STATUS", "TITLE", "WORKFLOW", "EVENT", "ID", "CREATED")
|
||||
for _, run := range runList.WorkflowRuns {
|
||||
createdTime, err := time.Parse(time.RFC3339, run.Created)
|
||||
if err != nil {
|
||||
createdTime = time.Now()
|
||||
}
|
||||
|
||||
timeStr := formatTimeSince(createdTime)
|
||||
|
||||
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\n",
|
||||
formatStatus(run.Status), run.Title, run.WorkflowID, run.Event, run.ID, timeStr); err != nil {
|
||||
return fmt.Errorf("failed to write run: %w", err)
|
||||
}
|
||||
tp.AddRow(formatStatus(run.Status), run.Title, run.WorkflowID, run.Event, fmt.Sprintf("%d", run.ID), timeStr)
|
||||
}
|
||||
|
||||
if err := w.Flush(); err != nil {
|
||||
return fmt.Errorf("failed to flush output: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return tp.Render()
|
||||
}
|
||||
|
||||
func runRunView(cmd *cobra.Command, args []string) error {
|
||||
|
|
@ -425,7 +489,7 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
showLog, _ := cmd.Flags().GetBool("log")
|
||||
jobIDStr, _ := cmd.Flags().GetString("job")
|
||||
showLogFailed, _ := cmd.Flags().GetBool("log-failed")
|
||||
jsonOutput, _ := cmd.Flags().GetBool("json")
|
||||
jsonRequested := wantJSON(cmd)
|
||||
|
||||
var jobID int64
|
||||
if jobIDStr != "" {
|
||||
|
|
@ -436,7 +500,7 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
}
|
||||
|
||||
if jsonOutput && (showLog || showLogFailed) {
|
||||
if jsonRequested && (showLog || showLogFailed) {
|
||||
return fmt.Errorf("--json cannot be used with --log or --log-failed")
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +514,7 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
|
||||
needsJobs := verbose || showLog || showLogFailed || jobID > 0
|
||||
|
||||
if jsonOutput {
|
||||
if jsonRequested {
|
||||
var runTasks []ActionTask
|
||||
if needsJobs {
|
||||
tasksEndpoint := fmt.Sprintf("/api/v1/repos/%s/%s/actions/tasks", owner, name)
|
||||
|
|
@ -487,33 +551,33 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
Run: run,
|
||||
Tasks: runTasks,
|
||||
}
|
||||
return writeJSON(payload)
|
||||
return outputJSON(cmd, payload)
|
||||
}
|
||||
|
||||
// Display run information
|
||||
fmt.Printf("Title: %s\n", run.Title)
|
||||
fmt.Printf("Workflow: %s\n", run.WorkflowID)
|
||||
fmt.Printf("Run: #%d\n", run.IndexInRepo)
|
||||
fmt.Printf("Status: %s\n", formatStatus(run.Status))
|
||||
fmt.Printf("Event: %s\n", run.Event)
|
||||
fmt.Printf("Ref: %s\n", run.PrettyRef)
|
||||
fmt.Fprintf(ios.Out, "Title: %s\n", run.Title)
|
||||
fmt.Fprintf(ios.Out, "Workflow: %s\n", run.WorkflowID)
|
||||
fmt.Fprintf(ios.Out, "Run: #%d\n", run.IndexInRepo)
|
||||
fmt.Fprintf(ios.Out, "Status: %s\n", formatStatus(run.Status))
|
||||
fmt.Fprintf(ios.Out, "Event: %s\n", run.Event)
|
||||
fmt.Fprintf(ios.Out, "Ref: %s\n", run.PrettyRef)
|
||||
|
||||
commit := run.CommitSHA
|
||||
if len(commit) > 8 {
|
||||
commit = commit[:8]
|
||||
}
|
||||
fmt.Printf("Commit: %s\n", commit)
|
||||
fmt.Fprintf(ios.Out, "Commit: %s\n", commit)
|
||||
|
||||
if createdTime, err := time.Parse(time.RFC3339, run.Created); err == nil {
|
||||
fmt.Printf("Created: %s\n", createdTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(ios.Out, "Created: %s\n", createdTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
if run.Started != "" {
|
||||
if startedTime, err := time.Parse(time.RFC3339, run.Started); err == nil {
|
||||
fmt.Printf("Started: %s\n", startedTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(ios.Out, "Started: %s\n", startedTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
}
|
||||
if updatedTime, err := time.Parse(time.RFC3339, run.Updated); err == nil {
|
||||
fmt.Printf("Updated: %s\n", updatedTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(ios.Out, "Updated: %s\n", updatedTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// Fetch jobs if needed for verbose, log, or job-specific views
|
||||
|
|
@ -536,7 +600,7 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
if len(runTasks) == 0 {
|
||||
fmt.Println("\nNo jobs found for this run")
|
||||
fmt.Fprintln(ios.Out, "\nNo jobs found for this run")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -557,14 +621,14 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// Case 1: --verbose (show job steps/details without logs)
|
||||
if verbose && !showLog && !showLogFailed {
|
||||
fmt.Println("\nJobs:")
|
||||
fmt.Fprintln(ios.Out, "\nJobs:")
|
||||
for _, task := range runTasks {
|
||||
fmt.Printf("\n %s - %s (ID: %d)\n", formatStatus(task.Status), task.Name, task.ID)
|
||||
fmt.Fprintf(ios.Out, "\n %s - %s (ID: %d)\n", formatStatus(task.Status), task.Name, task.ID)
|
||||
if startedTime, err := time.Parse(time.RFC3339, task.RunStartedAt); err == nil {
|
||||
fmt.Printf(" Started: %s\n", startedTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(ios.Out, " Started: %s\n", startedTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
if updatedTime, err := time.Parse(time.RFC3339, task.UpdatedAt); err == nil {
|
||||
fmt.Printf(" Updated: %s\n", updatedTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(ios.Out, " Updated: %s\n", updatedTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -574,7 +638,7 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
if showLog || showLogFailed {
|
||||
for _, task := range runTasks {
|
||||
if err := showJobLog(client, owner, name, task, showLogFailed); err != nil {
|
||||
fmt.Printf("\nError fetching log for job %s: %v\n", task.Name, err)
|
||||
fmt.Fprintf(ios.Out, "\nError fetching log for job %s: %v\n", task.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -583,15 +647,15 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
// Case 3: --job without --log or --verbose (show job details only)
|
||||
if jobID > 0 {
|
||||
task := runTasks[0]
|
||||
fmt.Println("\nJob Details:")
|
||||
fmt.Printf(" Name: %s\n", task.Name)
|
||||
fmt.Printf(" ID: %d\n", task.ID)
|
||||
fmt.Printf(" Status: %s\n", formatStatus(task.Status))
|
||||
fmt.Fprintln(ios.Out, "\nJob Details:")
|
||||
fmt.Fprintf(ios.Out, " Name: %s\n", task.Name)
|
||||
fmt.Fprintf(ios.Out, " ID: %d\n", task.ID)
|
||||
fmt.Fprintf(ios.Out, " Status: %s\n", formatStatus(task.Status))
|
||||
if startedTime, err := time.Parse(time.RFC3339, task.RunStartedAt); err == nil {
|
||||
fmt.Printf(" Started: %s\n", startedTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(ios.Out, " Started: %s\n", startedTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
if updatedTime, err := time.Parse(time.RFC3339, task.UpdatedAt); err == nil {
|
||||
fmt.Printf(" Updated: %s\n", updatedTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Fprintf(ios.Out, " Updated: %s\n", updatedTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -635,12 +699,12 @@ func runRunWatch(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
if run.Status != lastStatus {
|
||||
fmt.Printf("Status: %s\n", formatStatus(run.Status))
|
||||
fmt.Fprintf(ios.Out, "Status: %s\n", formatStatus(run.Status))
|
||||
lastStatus = run.Status
|
||||
}
|
||||
|
||||
if isRunComplete(run.Status) {
|
||||
fmt.Printf("Run #%d completed with status %s\n", run.IndexInRepo, formatStatus(run.Status))
|
||||
fmt.Fprintf(ios.Out, "Run #%d completed with status %s\n", run.IndexInRepo, formatStatus(run.Status))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -675,7 +739,7 @@ func runRunRerun(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to rerun workflow: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Rerun requested for run %d\n", runID)
|
||||
fmt.Fprintf(ios.Out, "✓ Rerun requested for run %d\n", runID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -706,7 +770,7 @@ func runRunCancel(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to cancel workflow run: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Cancel requested for run %d\n", runID)
|
||||
fmt.Fprintf(ios.Out, "✓ Cancel requested for run %d\n", runID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -715,10 +779,10 @@ func showJobLog(client *api.Client, owner, name string, task ActionTask, logFail
|
|||
logURL := fmt.Sprintf("https://%s/api/v1/repos/%s/%s/actions/jobs/%d/logs",
|
||||
client.Hostname(), owner, name, task.ID)
|
||||
|
||||
fmt.Printf("\n========================================\n")
|
||||
fmt.Printf("Job: %s (ID: %d)\n", task.Name, task.ID)
|
||||
fmt.Printf("Status: %s\n", formatStatus(task.Status))
|
||||
fmt.Printf("========================================\n\n")
|
||||
fmt.Fprintf(ios.Out, "\n========================================\n")
|
||||
fmt.Fprintf(ios.Out, "Job: %s (ID: %d)\n", task.Name, task.ID)
|
||||
fmt.Fprintf(ios.Out, "Status: %s\n", formatStatus(task.Status))
|
||||
fmt.Fprintf(ios.Out, "========================================\n\n")
|
||||
|
||||
// Use GetRawLog helper
|
||||
logContent, err := client.GetRawLog(logURL)
|
||||
|
|
@ -731,11 +795,11 @@ func showJobLog(client *api.Client, owner, name string, task ActionTask, logFail
|
|||
if logFailed {
|
||||
// TODO: Implement filtering for failed steps only
|
||||
// This would require parsing the log format and identifying failed step markers
|
||||
fmt.Println("Note: --log-failed filtering not yet implemented, showing all logs")
|
||||
fmt.Fprintln(ios.Out, "Note: --log-failed filtering not yet implemented, showing all logs")
|
||||
}
|
||||
|
||||
fmt.Print(logContent)
|
||||
fmt.Println()
|
||||
fmt.Fprint(ios.Out, logContent)
|
||||
fmt.Fprintln(ios.Out)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -848,34 +912,25 @@ func runWorkflowList(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
if len(allWorkflows) == 0 {
|
||||
if jsonOutput, _ := cmd.Flags().GetBool("json"); jsonOutput {
|
||||
return writeJSON(allWorkflows)
|
||||
if wantJSON(cmd) {
|
||||
return outputJSON(cmd, allWorkflows)
|
||||
}
|
||||
fmt.Println("No workflows found")
|
||||
fmt.Fprintln(ios.Out, "No workflows found")
|
||||
return nil
|
||||
}
|
||||
|
||||
if jsonOutput, _ := cmd.Flags().GetBool("json"); jsonOutput {
|
||||
return writeJSON(allWorkflows)
|
||||
if wantJSON(cmd) {
|
||||
return outputJSON(cmd, allWorkflows)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
if _, err := fmt.Fprintln(w, "NAME\tSTATE\tPATH"); err != nil {
|
||||
return fmt.Errorf("failed to write header: %w", err)
|
||||
}
|
||||
tp := ios.NewTablePrinter()
|
||||
tp.AddHeader("NAME", "STATE", "PATH")
|
||||
|
||||
for _, workflow := range allWorkflows {
|
||||
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\n",
|
||||
workflow.Name, workflow.State, workflow.Path); err != nil {
|
||||
return fmt.Errorf("failed to write workflow: %w", err)
|
||||
}
|
||||
tp.AddRow(workflow.Name, workflow.State, workflow.Path)
|
||||
}
|
||||
|
||||
if err := w.Flush(); err != nil {
|
||||
return fmt.Errorf("failed to flush output: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return tp.Render()
|
||||
}
|
||||
|
||||
func runWorkflowView(cmd *cobra.Command, args []string) error {
|
||||
|
|
@ -901,8 +956,6 @@ func runWorkflowView(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
jsonOutput, _ := cmd.Flags().GetBool("json")
|
||||
|
||||
var latestRun *ActionRun
|
||||
|
||||
// Get the latest run for this workflow
|
||||
|
|
@ -912,7 +965,7 @@ func runWorkflowView(cmd *cobra.Command, args []string) error {
|
|||
latestRun = &runList.WorkflowRuns[0]
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
if wantJSON(cmd) {
|
||||
payload := struct {
|
||||
Workflow *Workflow `json:"workflow"`
|
||||
LatestRun *ActionRun `json:"latest_run,omitempty"`
|
||||
|
|
@ -920,21 +973,21 @@ func runWorkflowView(cmd *cobra.Command, args []string) error {
|
|||
Workflow: workflow,
|
||||
LatestRun: latestRun,
|
||||
}
|
||||
return writeJSON(payload)
|
||||
return outputJSON(cmd, payload)
|
||||
}
|
||||
|
||||
// Display workflow information
|
||||
fmt.Printf("Name: %s\n", workflow.Name)
|
||||
fmt.Printf("Path: %s\n", workflow.Path)
|
||||
fmt.Printf("State: %s\n", workflow.State)
|
||||
fmt.Fprintf(ios.Out, "Name: %s\n", workflow.Name)
|
||||
fmt.Fprintf(ios.Out, "Path: %s\n", workflow.Path)
|
||||
fmt.Fprintf(ios.Out, "State: %s\n", workflow.State)
|
||||
|
||||
if latestRun != nil {
|
||||
fmt.Printf("\nLatest run:\n")
|
||||
fmt.Printf(" Status: %s\n", formatStatus(latestRun.Status))
|
||||
fmt.Printf(" Event: %s\n", latestRun.Event)
|
||||
fmt.Printf(" Ref: %s\n", latestRun.PrettyRef)
|
||||
fmt.Fprintf(ios.Out, "\nLatest run:\n")
|
||||
fmt.Fprintf(ios.Out, " Status: %s\n", formatStatus(latestRun.Status))
|
||||
fmt.Fprintf(ios.Out, " Event: %s\n", latestRun.Event)
|
||||
fmt.Fprintf(ios.Out, " Ref: %s\n", latestRun.PrettyRef)
|
||||
if createdTime, err := time.Parse(time.RFC3339, latestRun.Created); err == nil {
|
||||
fmt.Printf(" Created: %s\n", formatTimeSince(createdTime))
|
||||
fmt.Fprintf(ios.Out, " Created: %s\n", formatTimeSince(createdTime))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1006,12 +1059,12 @@ func runWorkflowRun(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to trigger workflow: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Workflow '%s' triggered successfully\n", workflowIdentifier)
|
||||
fmt.Printf(" Branch/Tag: %s\n", ref)
|
||||
fmt.Fprintf(ios.Out, "✓ Workflow '%s' triggered successfully\n", workflowIdentifier)
|
||||
fmt.Fprintf(ios.Out, " Branch/Tag: %s\n", ref)
|
||||
if len(inputs) > 0 {
|
||||
fmt.Println(" Inputs:")
|
||||
fmt.Fprintln(ios.Out, " Inputs:")
|
||||
for key, value := range inputs {
|
||||
fmt.Printf(" %s: %s\n", key, value)
|
||||
fmt.Fprintf(ios.Out, " %s: %s\n", key, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1059,7 +1112,7 @@ func runWorkflowEnable(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to enable workflow: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Workflow '%s' enabled\n", workflow.Name)
|
||||
fmt.Fprintf(ios.Out, "✓ Workflow '%s' enabled\n", workflow.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1104,7 +1157,7 @@ func runWorkflowDisable(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to disable workflow: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Workflow '%s' disabled\n", workflow.Name)
|
||||
fmt.Fprintf(ios.Out, "✓ Workflow '%s' disabled\n", workflow.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1170,24 +1223,16 @@ func runActionsSecretList(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
if len(secrets) == 0 {
|
||||
fmt.Println("No secrets found")
|
||||
fmt.Fprintln(ios.Out, "No secrets found")
|
||||
return nil
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
if _, err := fmt.Fprintln(w, "NAME\tCREATED"); err != nil {
|
||||
return fmt.Errorf("failed to write header: %w", err)
|
||||
}
|
||||
tp := ios.NewTablePrinter()
|
||||
tp.AddHeader("NAME", "CREATED")
|
||||
for _, secret := range secrets {
|
||||
if _, err := fmt.Fprintf(w, "%s\t%s\n", secret.Name, secret.Created.Format("2006-01-02 15:04:05")); err != nil {
|
||||
return fmt.Errorf("failed to write secret: %w", err)
|
||||
}
|
||||
tp.AddRow(secret.Name, secret.Created.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
return fmt.Errorf("failed to flush output: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return tp.Render()
|
||||
}
|
||||
|
||||
func runActionsSecretCreate(cmd *cobra.Command, args []string) error {
|
||||
|
|
@ -1210,7 +1255,7 @@ func runActionsSecretCreate(cmd *cobra.Command, args []string) error {
|
|||
secretName := args[0]
|
||||
|
||||
// Read secret value from stdin
|
||||
fmt.Print("Enter secret value: ")
|
||||
fmt.Fprint(ios.ErrOut, "Enter secret value: ")
|
||||
var secretValue string
|
||||
_, err = fmt.Scanln(&secretValue)
|
||||
if err != nil {
|
||||
|
|
@ -1227,7 +1272,7 @@ func runActionsSecretCreate(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to create secret: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Secret '%s' created successfully\n", secretName)
|
||||
fmt.Fprintf(ios.Out, "Secret '%s' created successfully\n", secretName)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1255,15 +1300,57 @@ func runActionsSecretDelete(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to delete secret: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Secret '%s' deleted successfully\n", secretName)
|
||||
fmt.Fprintf(ios.Out, "Secret '%s' deleted successfully\n", secretName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Variable command implementations
|
||||
|
||||
// ActionVariable represents a repository action variable from the API
|
||||
type ActionVariable struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"data"`
|
||||
}
|
||||
|
||||
func runActionsVariableList(cmd *cobra.Command, args []string) error {
|
||||
// Note: The SDK doesn't have a ListRepoActionVariable method yet
|
||||
return fmt.Errorf("listing variables is not yet supported in the SDK")
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
client, err := api.NewClientFromConfig(cfg, "", getDetectedHost())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create client: %w", err)
|
||||
}
|
||||
|
||||
repo, _ := cmd.Flags().GetString("repo")
|
||||
owner, name, err := parseRepo(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoint := fmt.Sprintf("/api/v1/repos/%s/%s/actions/variables", owner, name)
|
||||
|
||||
var variables []ActionVariable
|
||||
if err := client.GetJSON(endpoint, &variables); err != nil {
|
||||
return fmt.Errorf("failed to list variables: %w", err)
|
||||
}
|
||||
|
||||
if wantJSON(cmd) {
|
||||
return outputJSON(cmd, variables)
|
||||
}
|
||||
|
||||
if len(variables) == 0 {
|
||||
fmt.Fprintln(ios.Out, "No variables found")
|
||||
return nil
|
||||
}
|
||||
|
||||
tp := ios.NewTablePrinter()
|
||||
tp.AddHeader("NAME", "VALUE")
|
||||
for _, v := range variables {
|
||||
tp.AddRow(v.Name, v.Value)
|
||||
}
|
||||
return tp.Render()
|
||||
}
|
||||
|
||||
func runActionsVariableGet(cmd *cobra.Command, args []string) error {
|
||||
|
|
@ -1290,7 +1377,7 @@ func runActionsVariableGet(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to get variable: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%s=%s\n", variable.Name, variable.Value)
|
||||
fmt.Fprintf(ios.Out, "%s=%s\n", variable.Name, variable.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1319,7 +1406,7 @@ func runActionsVariableCreate(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to create variable: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Variable '%s' created successfully\n", variableName)
|
||||
fmt.Fprintf(ios.Out, "Variable '%s' created successfully\n", variableName)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1348,7 +1435,7 @@ func runActionsVariableUpdate(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to update variable: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Variable '%s' updated successfully\n", variableName)
|
||||
fmt.Fprintf(ios.Out, "Variable '%s' updated successfully\n", variableName)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1376,6 +1463,6 @@ func runActionsVariableDelete(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to delete variable: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Variable '%s' deleted successfully\n", variableName)
|
||||
fmt.Fprintf(ios.Out, "Variable '%s' deleted successfully\n", variableName)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue