feat: actions support
This commit is contained in:
parent
ca17526594
commit
8db012cb7a
2 changed files with 127 additions and 53 deletions
|
|
@ -189,9 +189,10 @@ func init() {
|
|||
addRepoFlags(runListCmd)
|
||||
runListCmd.Flags().IntP("limit", "L", 20, "Maximum number of runs to list")
|
||||
addRepoFlags(runViewCmd)
|
||||
runViewCmd.Flags().BoolP("verbose", "v", false, "Show job details")
|
||||
runViewCmd.Flags().BoolP("log", "", false, "Show logs for all jobs")
|
||||
runViewCmd.Flags().Int64P("job", "j", 0, "Show logs for a specific job ID")
|
||||
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")
|
||||
|
||||
// Add flags for secret commands
|
||||
addRepoFlags(actionsSecretListCmd)
|
||||
|
|
@ -294,7 +295,17 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
|
||||
verbose, _ := cmd.Flags().GetBool("verbose")
|
||||
showLog, _ := cmd.Flags().GetBool("log")
|
||||
jobID, _ := cmd.Flags().GetInt64("job")
|
||||
jobIDStr, _ := cmd.Flags().GetString("job")
|
||||
showLogFailed, _ := cmd.Flags().GetBool("log-failed")
|
||||
|
||||
var jobID int64
|
||||
if jobIDStr != "" {
|
||||
var err error
|
||||
jobID, err = strconv.ParseInt(jobIDStr, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid job ID: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Call the API endpoint directly since SDK doesn't have it yet
|
||||
endpoint := fmt.Sprintf("/api/v1/repos/%s/%s/actions/runs/%d", owner, name, runID)
|
||||
|
|
@ -330,8 +341,12 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
fmt.Printf("Updated: %s\n", updatedTime.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// If --log or --job flag is provided, or --verbose, fetch jobs
|
||||
if verbose || showLog || jobID > 0 {
|
||||
// Fetch jobs if needed for verbose, log, or job-specific views
|
||||
needsJobs := verbose || showLog || showLogFailed || jobID > 0
|
||||
if !needsJobs {
|
||||
return nil
|
||||
}
|
||||
|
||||
tasksEndpoint := fmt.Sprintf("/api/v1/repos/%s/%s/actions/tasks", owner, name)
|
||||
var taskList ActionTaskList
|
||||
if err := client.GetJSON(tasksEndpoint, &taskList); err != nil {
|
||||
|
|
@ -351,8 +366,23 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Show jobs if verbose (and not showing logs)
|
||||
if verbose && !showLog && jobID == 0 {
|
||||
// If --job is specified, filter to that job
|
||||
if jobID > 0 {
|
||||
var found bool
|
||||
for _, task := range runTasks {
|
||||
if task.ID == jobID {
|
||||
runTasks = []ActionTask{task}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("job %d not found in this run", jobID)
|
||||
}
|
||||
}
|
||||
|
||||
// Case 1: --verbose (show job steps/details without logs)
|
||||
if verbose && !showLog && !showLogFailed {
|
||||
fmt.Println("\nJobs:")
|
||||
for _, task := range runTasks {
|
||||
fmt.Printf("\n %s - %s (ID: %d)\n", formatStatus(task.Status), task.Name, task.ID)
|
||||
|
|
@ -366,35 +396,35 @@ func runRunView(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Show logs for specific job or all jobs
|
||||
if showLog || jobID > 0 {
|
||||
tasksToShow := runTasks
|
||||
if jobID > 0 {
|
||||
// Filter to specific job
|
||||
tasksToShow = nil
|
||||
// Case 2: --log or --log-failed (show logs)
|
||||
if showLog || showLogFailed {
|
||||
for _, task := range runTasks {
|
||||
if task.ID == jobID {
|
||||
tasksToShow = []ActionTask{task}
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(tasksToShow) == 0 {
|
||||
return fmt.Errorf("job %d not found in this run", jobID)
|
||||
}
|
||||
}
|
||||
|
||||
for _, task := range tasksToShow {
|
||||
if err := showJobLog(client, owner, name, run.IndexInRepo, task); err != nil {
|
||||
if err := showJobLog(client, owner, name, run.IndexInRepo, task, showLogFailed); err != nil {
|
||||
fmt.Printf("\nError fetching log for job %s: %v\n", task.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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))
|
||||
if startedTime, err := time.Parse(time.RFC3339, task.RunStartedAt); err == nil {
|
||||
fmt.Printf(" 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"))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func showJobLog(client *api.Client, owner, name string, runNumber int64, task ActionTask) 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
|
||||
logURL := fmt.Sprintf("https://%s/%s/%s/actions/runs/%d/jobs/%d/logs",
|
||||
client.Hostname(), owner, name, runNumber, task.ID)
|
||||
|
|
@ -410,6 +440,14 @@ func showJobLog(client *api.Client, owner, name string, runNumber int64, task Ac
|
|||
return err
|
||||
}
|
||||
|
||||
// If --log-failed, filter to only show failed steps
|
||||
// For now, just show all logs (filtering failed steps would require parsing the log format)
|
||||
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.Print(logContent)
|
||||
fmt.Println()
|
||||
|
||||
|
|
|
|||
|
|
@ -84,3 +84,39 @@ func (c *Client) GetJSON(path string, result any) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRawLog performs a GET request and returns the raw response body as string
|
||||
func (c *Client) GetRawLog(url string) (string, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
// Set authentication header
|
||||
if c.token != "" {
|
||||
req.Header.Set("Authorization", "token "+c.token)
|
||||
}
|
||||
|
||||
httpClient := &http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to perform request: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := resp.Body.Close(); closeErr != nil && err == nil {
|
||||
err = fmt.Errorf("failed to close response body: %w", closeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return "", fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
return string(bodyBytes), nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue