1
0
mirror of https://github.com/woodpecker-ci/woodpecker.git synced 2024-11-24 08:02:18 +02:00

Cli fix pipeline logs (#3913)

Co-authored-by: Thomas Anderson <127358482+zc-devs@users.noreply.github.com>
Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
smainz 2024-07-18 20:39:18 +02:00 committed by GitHub
parent 7b7c83d040
commit 49c2029cad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 118 additions and 21 deletions

View File

@ -161,3 +161,47 @@ func ParseKeyPair(p []string) map[string]string {
}
return params
}
/*
ParseStep parses the step id form a string which may either be the step PID (step number) or a step name.
These rules apply:
- Step ID take precedence over step name when searching for a match.
- First match is used, when there are multiple steps with the same name.
Strictly speaking, this is not parsing, but a lookup.
TODO: Use PID instead of StepID
*/
func ParseStep(client woodpecker.Client, repoID, number int64, stepArg string) (stepID int64, err error) {
pipeline, err := client.Pipeline(repoID, number)
if err != nil {
return 0, err
}
stepID, err = strconv.ParseInt(stepArg, 10, 64)
// TODO: for 3.0 do "stepPID, err := strconv.ParseInt(stepArg, 10, 64)"
if err == nil {
return stepID, nil
/*
// TODO: for 3.0
for _, wf := range pipeline.Workflows {
for _, step := range wf.Children {
if int64(step.PID) == stepPID {
return step.ID, nil
}
}
}
*/
}
for _, wf := range pipeline.Workflows {
for _, step := range wf.Children {
if step.Name == stepArg {
return step.ID, nil
}
}
}
return 0, fmt.Errorf("no step with number or name '%s' found", stepArg)
}

View File

@ -17,18 +17,22 @@ package pipeline
import (
"context"
"fmt"
"os"
"strconv"
"text/template"
"github.com/urfave/cli/v3"
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
)
var pipelineLogsCmd = &cli.Command{
Name: "logs",
Usage: "show pipeline logs",
ArgsUsage: "<repo-id|repo-full-name> [pipeline] [stepID]",
Action: pipelineLogs,
ArgsUsage: "<repo-id|repo-full-name> <pipeline> [step-id|step-name]",
// TODO: for v3.0 do `ArgsUsage: "<repo-id|repo-full-name> <pipeline> [step-number|step-name]",`
Action: pipelineLogs,
}
func pipelineLogs(ctx context.Context, c *cli.Command) error {
@ -37,31 +41,73 @@ func pipelineLogs(ctx context.Context, c *cli.Command) error {
if err != nil {
return err
}
if len(repoIDOrFullName) == 0 {
return fmt.Errorf("missing required argument repo-id / repo-full-name")
}
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
if err != nil {
return err
return fmt.Errorf("invalid repo '%s': %w ", repoIDOrFullName, err)
}
numberArgIndex := 1
number, err := strconv.ParseInt(c.Args().Get(numberArgIndex), 10, 64)
pipelineArg := c.Args().Get(1)
if len(pipelineArg) == 0 {
return fmt.Errorf("missing required argument pipeline")
}
number, err := strconv.ParseInt(pipelineArg, 10, 64)
if err != nil {
return fmt.Errorf("invalid pipeline '%s': %w", pipelineArg, err)
}
stepArg := c.Args().Get(2) //nolint:mnd
if len(stepArg) == 0 {
return showPipelineLog(client, repoID, number)
}
step, err := internal.ParseStep(client, repoID, number, stepArg)
if err != nil {
return fmt.Errorf("invalid step '%s': %w", stepArg, err)
}
return showStepLog(client, repoID, number, step)
}
func showPipelineLog(client woodpecker.Client, repoID, number int64) error {
pipeline, err := client.Pipeline(repoID, number)
if err != nil {
return err
}
stepArgIndex := 2
step, err := strconv.ParseInt(c.Args().Get(stepArgIndex), 10, 64)
tmpl, err := template.New("_").Parse(tmplPipelineLogs + "\n")
if err != nil {
return err
}
for _, workflow := range pipeline.Workflows {
for _, step := range workflow.Children {
if err := tmpl.Execute(os.Stdout, map[string]any{"workflow": workflow, "step": step}); err != nil {
return err
}
err := showStepLog(client, repoID, number, step.ID)
if err != nil {
return err
}
}
}
return nil
}
func showStepLog(client woodpecker.Client, repoID, number, step int64) error {
logs, err := client.StepLogEntries(repoID, number, step)
if err != nil {
return err
}
for _, log := range logs {
fmt.Print(string(log.Data))
fmt.Println(string(log.Data))
}
return nil
}
// template for pipeline ps information.
var tmplPipelineLogs = "\x1b[33m{{ .workflow.Name }} > {{ .step.Name }} (#{{ .step.PID }}):\x1b[0m"

View File

@ -16,6 +16,7 @@ package pipeline
import (
"context"
"fmt"
"os"
"strconv"
"text/template"
@ -29,7 +30,7 @@ import (
var pipelinePsCmd = &cli.Command{
Name: "ps",
Usage: "show pipeline steps",
ArgsUsage: "<repo-id|repo-full-name> [pipeline]",
ArgsUsage: "<repo-id|repo-full-name> <pipeline>",
Action: pipelinePs,
Flags: []cli.Flag{common.FormatFlag(tmplPipelinePs)},
}
@ -42,7 +43,7 @@ func pipelinePs(ctx context.Context, c *cli.Command) error {
}
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
if err != nil {
return err
return fmt.Errorf("invalid repo '%s': %w", repoIDOrFullName, err)
}
pipelineArg := c.Args().Get(1)
@ -59,7 +60,7 @@ func pipelinePs(ctx context.Context, c *cli.Command) error {
} else {
number, err = strconv.ParseInt(pipelineArg, 10, 64)
if err != nil {
return err
return fmt.Errorf("invalid pipeline '%s': %w", pipelineArg, err)
}
}
@ -73,9 +74,9 @@ func pipelinePs(ctx context.Context, c *cli.Command) error {
return err
}
for _, step := range pipeline.Workflows {
for _, child := range step.Children {
if err := tmpl.Execute(os.Stdout, child); err != nil {
for _, workflow := range pipeline.Workflows {
for _, step := range workflow.Children {
if err := tmpl.Execute(os.Stdout, map[string]any{"workflow": workflow, "step": step}); err != nil {
return err
}
}
@ -84,8 +85,11 @@ func pipelinePs(ctx context.Context, c *cli.Command) error {
return nil
}
// Template for pipeline ps information.
var tmplPipelinePs = "\x1b[33mStep #{{ .PID }} \x1b[0m" + `
Step: {{ .Name }}
State: {{ .State }}
// template for pipeline ps information.
var tmplPipelinePs = "\x1b[33m{{ .workflow.Name }} > {{ .step.Name }} (#{{ .step.PID }}):\x1b[0m" + `
Step: {{ .step.Name }}
Started: {{ .step.Started }}
Stopped: {{ .step.Stopped }}
Type: {{ .step.Type }}
State: {{ .step.State }}
`

View File

@ -345,9 +345,12 @@ Message: {{ .Message }}
show pipeline steps
**--format**="": format output (default: Step #{{ .PID }} 
Step: {{ .Name }}
State: {{ .State }}
**--format**="": format output (default: {{ .workflow.Name }} > {{ .step.Name }} (#{{ .step.PID }}):
Step: {{ .step.Name }}
Started: {{ .step.Started }}
Stopped: {{ .step.Stopped }}
Type: {{ .step.Type }}
State: {{ .step.State }}
)
### create