mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-04-26 21:44:44 +00: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:
parent
7b7c83d040
commit
49c2029cad
4 changed files with 118 additions and 21 deletions
|
@ -161,3 +161,47 @@ func ParseKeyPair(p []string) map[string]string {
|
||||||
}
|
}
|
||||||
return params
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -17,18 +17,22 @@ package pipeline
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
|
|
||||||
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
"go.woodpecker-ci.org/woodpecker/v2/cli/internal"
|
||||||
|
"go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pipelineLogsCmd = &cli.Command{
|
var pipelineLogsCmd = &cli.Command{
|
||||||
Name: "logs",
|
Name: "logs",
|
||||||
Usage: "show pipeline logs",
|
Usage: "show pipeline logs",
|
||||||
ArgsUsage: "<repo-id|repo-full-name> [pipeline] [stepID]",
|
ArgsUsage: "<repo-id|repo-full-name> <pipeline> [step-id|step-name]",
|
||||||
Action: pipelineLogs,
|
// 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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(repoIDOrFullName) == 0 {
|
||||||
|
return fmt.Errorf("missing required argument repo-id / repo-full-name")
|
||||||
|
}
|
||||||
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("invalid repo '%s': %w ", repoIDOrFullName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
numberArgIndex := 1
|
pipelineArg := c.Args().Get(1)
|
||||||
number, err := strconv.ParseInt(c.Args().Get(numberArgIndex), 10, 64)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stepArgIndex := 2
|
tmpl, err := template.New("_").Parse(tmplPipelineLogs + "\n")
|
||||||
step, err := strconv.ParseInt(c.Args().Get(stepArgIndex), 10, 64)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
logs, err := client.StepLogEntries(repoID, number, step)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, log := range logs {
|
for _, log := range logs {
|
||||||
fmt.Print(string(log.Data))
|
fmt.Println(string(log.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// template for pipeline ps information.
|
||||||
|
var tmplPipelineLogs = "\x1b[33m{{ .workflow.Name }} > {{ .step.Name }} (#{{ .step.PID }}):\x1b[0m"
|
||||||
|
|
|
@ -16,6 +16,7 @@ package pipeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
@ -29,7 +30,7 @@ import (
|
||||||
var pipelinePsCmd = &cli.Command{
|
var pipelinePsCmd = &cli.Command{
|
||||||
Name: "ps",
|
Name: "ps",
|
||||||
Usage: "show pipeline steps",
|
Usage: "show pipeline steps",
|
||||||
ArgsUsage: "<repo-id|repo-full-name> [pipeline]",
|
ArgsUsage: "<repo-id|repo-full-name> <pipeline>",
|
||||||
Action: pipelinePs,
|
Action: pipelinePs,
|
||||||
Flags: []cli.Flag{common.FormatFlag(tmplPipelinePs)},
|
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)
|
repoID, err := internal.ParseRepo(client, repoIDOrFullName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("invalid repo '%s': %w", repoIDOrFullName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pipelineArg := c.Args().Get(1)
|
pipelineArg := c.Args().Get(1)
|
||||||
|
@ -59,7 +60,7 @@ func pipelinePs(ctx context.Context, c *cli.Command) error {
|
||||||
} else {
|
} else {
|
||||||
number, err = strconv.ParseInt(pipelineArg, 10, 64)
|
number, err = strconv.ParseInt(pipelineArg, 10, 64)
|
||||||
if err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, step := range pipeline.Workflows {
|
for _, workflow := range pipeline.Workflows {
|
||||||
for _, child := range step.Children {
|
for _, step := range workflow.Children {
|
||||||
if err := tmpl.Execute(os.Stdout, child); err != nil {
|
if err := tmpl.Execute(os.Stdout, map[string]any{"workflow": workflow, "step": step}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,8 +85,11 @@ func pipelinePs(ctx context.Context, c *cli.Command) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template for pipeline ps information.
|
// template for pipeline ps information.
|
||||||
var tmplPipelinePs = "\x1b[33mStep #{{ .PID }} \x1b[0m" + `
|
var tmplPipelinePs = "\x1b[33m{{ .workflow.Name }} > {{ .step.Name }} (#{{ .step.PID }}):\x1b[0m" + `
|
||||||
Step: {{ .Name }}
|
Step: {{ .step.Name }}
|
||||||
State: {{ .State }}
|
Started: {{ .step.Started }}
|
||||||
|
Stopped: {{ .step.Stopped }}
|
||||||
|
Type: {{ .step.Type }}
|
||||||
|
State: {{ .step.State }}
|
||||||
`
|
`
|
||||||
|
|
|
@ -345,9 +345,12 @@ Message: {{ .Message }}
|
||||||
|
|
||||||
show pipeline steps
|
show pipeline steps
|
||||||
|
|
||||||
**--format**="": format output (default: [33mStep #{{ .PID }} [0m
|
**--format**="": format output (default: [33m{{ .workflow.Name }} > {{ .step.Name }} (#{{ .step.PID }}):[0m
|
||||||
Step: {{ .Name }}
|
Step: {{ .step.Name }}
|
||||||
State: {{ .State }}
|
Started: {{ .step.Started }}
|
||||||
|
Stopped: {{ .step.Stopped }}
|
||||||
|
Type: {{ .step.Type }}
|
||||||
|
State: {{ .step.State }}
|
||||||
)
|
)
|
||||||
|
|
||||||
### create
|
### create
|
||||||
|
|
Loading…
Reference in a new issue