feat: add actions run watch rerun and cancel
This commit is contained in:
parent
3ccef4e1c6
commit
c2ee338f1c
1 changed files with 152 additions and 0 deletions
152
cmd/actions.go
152
cmd/actions.go
|
|
@ -109,6 +109,30 @@ var runViewCmd = &cobra.Command{
|
||||||
RunE: runRunView,
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
// Workflow commands
|
// Workflow commands
|
||||||
var workflowCmd = &cobra.Command{
|
var workflowCmd = &cobra.Command{
|
||||||
Use: "workflow",
|
Use: "workflow",
|
||||||
|
|
@ -222,6 +246,9 @@ func init() {
|
||||||
actionsCmd.AddCommand(runCmd)
|
actionsCmd.AddCommand(runCmd)
|
||||||
runCmd.AddCommand(runListCmd)
|
runCmd.AddCommand(runListCmd)
|
||||||
runCmd.AddCommand(runViewCmd)
|
runCmd.AddCommand(runViewCmd)
|
||||||
|
runCmd.AddCommand(runWatchCmd)
|
||||||
|
runCmd.AddCommand(runRerunCmd)
|
||||||
|
runCmd.AddCommand(runCancelCmd)
|
||||||
|
|
||||||
// Add workflow commands (gh workflow compatible)
|
// Add workflow commands (gh workflow compatible)
|
||||||
actionsCmd.AddCommand(workflowCmd)
|
actionsCmd.AddCommand(workflowCmd)
|
||||||
|
|
@ -253,6 +280,10 @@ func init() {
|
||||||
runViewCmd.Flags().StringP("job", "j", "", "View a specific job ID from a run")
|
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().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")
|
runViewCmd.Flags().Bool("json", false, "Output workflow run as JSON")
|
||||||
|
addRepoFlags(runWatchCmd)
|
||||||
|
runWatchCmd.Flags().DurationP("interval", "i", 5*time.Second, "Polling interval")
|
||||||
|
addRepoFlags(runRerunCmd)
|
||||||
|
addRepoFlags(runCancelCmd)
|
||||||
|
|
||||||
// Add flags for workflow commands
|
// Add flags for workflow commands
|
||||||
addRepoFlags(workflowListCmd)
|
addRepoFlags(workflowListCmd)
|
||||||
|
|
@ -545,6 +576,118 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runRunWatch(cmd *cobra.Command, args []string) error {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
runID, err := strconv.ParseInt(args[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid run ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
interval, _ := cmd.Flags().GetDuration("interval")
|
||||||
|
if interval <= 0 {
|
||||||
|
return fmt.Errorf("interval must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("/api/v1/repos/%s/%s/actions/runs/%d", owner, name, runID)
|
||||||
|
|
||||||
|
var lastStatus string
|
||||||
|
for {
|
||||||
|
var run ActionRun
|
||||||
|
if err := client.GetJSON(endpoint, &run); err != nil {
|
||||||
|
return fmt.Errorf("failed to get run: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if run.Status != lastStatus {
|
||||||
|
fmt.Printf("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))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRunRerun(cmd *cobra.Command, args []string) error {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
runID, err := strconv.ParseInt(args[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid run ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("/api/v1/repos/%s/%s/actions/runs/%d/rerun", owner, name, runID)
|
||||||
|
if err := client.PostJSON(endpoint, nil, nil); err != nil {
|
||||||
|
return fmt.Errorf("failed to rerun workflow: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("✓ Rerun requested for run %d\n", runID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRunCancel(cmd *cobra.Command, args []string) error {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
runID, err := strconv.ParseInt(args[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid run ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("/api/v1/repos/%s/%s/actions/runs/%d/cancel", owner, name, runID)
|
||||||
|
if err := client.PostJSON(endpoint, nil, nil); err != nil {
|
||||||
|
return fmt.Errorf("failed to cancel workflow run: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("✓ Cancel requested for run %d\n", runID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func showJobLog(client *api.Client, owner, name string, runNumber int64, task ActionTask, logFailed bool) error {
|
func showJobLog(client *api.Client, owner, name string, runNumber int64, task ActionTask, logFailed bool) error {
|
||||||
// Fetch log from /repos/{owner}/{repo}/actions/runs/{run_number}/jobs/{job_id}/logs
|
// Fetch log from /repos/{owner}/{repo}/actions/runs/{run_number}/jobs/{job_id}/logs
|
||||||
logURL := fmt.Sprintf("https://%s/%s/%s/actions/runs/%d/jobs/%d/logs",
|
logURL := fmt.Sprintf("https://%s/%s/%s/actions/runs/%d/jobs/%d/logs",
|
||||||
|
|
@ -594,6 +737,15 @@ func formatStatus(status string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isRunComplete(status string) bool {
|
||||||
|
switch status {
|
||||||
|
case "success", "failure", "cancelled", "skipped":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func formatTimeSince(t time.Time) string {
|
func formatTimeSince(t time.Time) string {
|
||||||
duration := time.Since(t)
|
duration := time.Since(t)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue