refactor: reorganize console output (#10876) · updatecli/updatecli@4e87a4a · GitHub
Skip to content

Commit 4e87a4a

Browse files
authored
refactor: reorganize console output (#10876)
* refactor(log): add internal logging package with stackable capture Grow pkg/core/log into the logging entry point for Updatecli, still backed by logrus: - level functions mirroring the logrus API used across the code base - Capture(w) copies log lines to w until stopped; captures nest, so a pipeline-wide capture can wrap the per-resource ones - Progress() for transient output that should reach the terminal only - Title and Section keep the meaning of headers instead of building ASCII art at every call site The global logrus logger now writes through the same output, so dependencies logging with logrus still land in captures. Sources, conditions and targets use Capture instead of swapping the global logrus output and resetting it to a hardcoded os.Stdout. * refactor(log): stop plugins writing to stdout directly Output written with fmt.Print or to os.Stdout bypasses the logger, so it never reaches the console output attached to reports. - path walking and parsing failures in autodiscovery are now warnings - action and merge errors are logged as errors - drop a stray empty line in the dockerimage source and a duplicate error message in the npm resource, the error is already returned - git push progress goes to log.Progress, terminal only forbidigo now rejects fmt.Print* and os.Stdout/os.Stderr outside cmd/, the log package and tests. The interactive udash token prompt keeps writing to the terminal on purpose. * refactor(log): migrate from logrus to internal log package Mechanical swap of every logrus call for the matching pkg/core/log function, the log package mirrors the logrus API so the console output is unchanged. Tests now assert on log.Capture output instead of swapping the logrus output or installing a logrus test hook. depguard rejects new logrus imports outside pkg/core/log. * feat(report): capture full console output per pipeline Per-resource console output misses why a pipeline failed: resource errors, pipeline errors, git push and pull request output are all logged outside of any source, condition or target. Report gains a ConsoleOutput field holding the console output of the whole pipeline: - the pipeline run, including resource errors and the engine failure message - actions and their cleanup - git push and working branch cleanup, attributed to every pipeline with a target on that repository branch Push and cleanup errors are now logged where they happen so they end up in that output. Only the last 1MiB is kept, the end of a log being where a failure is explained. The per-branch push and cleanup logic moves into pushSCMBranch and pruneSCMBranch so the capture can wrap it. * refactor(log): use log/slog as logging backend The log package now logs through a log/slog handler writing the same plain text lines as before, so the console output is unchanged. Messages are only formatted when their level is enabled. logrus is still a dependency of helm, sops, testcontainers and others, so the global logrus logger stays routed through the same output, with the same format and level. It's the only place importing logrus. * feat(log): style terminal output with lipgloss Titles, section headers and log levels are now rendered with lipgloss on a color terminal: titles in a rounded box, sections with a colored category and a rule, levels as colored labels. The basic ANSI colors follow the terminal theme. Output to a non-terminal, or with NO_COLOR set, stays the exact same plain text as before, so CI logs don't change. Captures always get plain text. Previously a run on a color terminal stored ANSI codes in the console output of reports. Title and Section are passed to the slog handler as records with a kind, so the handler decides how to render them. lipgloss replaces the direct use of fatih/color. * refactor(log): route every console banner through the log package Banners were built by hand at each call site with strings.Repeat and strings.ToTitle, in five slightly different styles. They now all go through the log package, from the most to the least prominent: - Title: major steps, such as "Pipeline" or "Auto Discovery" - PipelineTitle: the name of the pipeline about to run - Heading: stages such as "Actions", "Pushing Git changes", "Report", "Run Summary", "Version" and every autodiscovery crawler - Section: sources, conditions and targets - Subheading: the errors of a pipeline, the changelog in debug mode Changing the look of the console output now only touches pkg/core/log/format.go. The plain text output keeps the same banners, with the whitespace around headings normalized and the underline matching the heading length. The summary banner of the report template becomes a heading. * refactor(transformer): log transformer steps at debug level Every source, condition and target with transformers logged a header and one line per step at info level, on every run, even when the value didn't change. The header and the per-step lines move to debug. At info level, a single line shows the net transformation, and only when the value changed: it explains why a value differs from its source, so it stays visible in the terminal and in the console output sent to udash, where debug is rarely enabled. * refactor(source): log source cache hits at debug level A cache hit is an implementation detail: the source result is the same either way, so the message is only useful when debugging. * feat(autodiscovery): show when autodiscovery is disabled When no pipeline defines autodiscovery crawlers, the "Auto Discovery" title was followed by nothing, which looked like a step that ran and silently failed. It's now followed by "disabled". * feat(log): make pipelines the most prominent unit of the console output Step titles such as "Pipeline" or "Auto Discovery" appear once per run but were the most prominent banners, while pipelines, the unit people scan for, were a small badge. - step titles become a dim full-width rule on a color terminal, and a single "== PIPELINE ==" line in plain text - the pipeline title opens with a colored bar, its short ID and the dry run mode folded into a second line; plain text keeps the box and the full ID - each pipeline closes with its result and duration, colored by result on a color terminal, "=> CHANGED (4.2s)" in plain text, so it's clear where a pipeline ends; it's part of the pipeline console output too * feat(log): open pipelines with a full-width band and a position counter The pipeline title was still hard to spot: its bar was one character wide and its bold name weighed the same as the source and target headers repeated around it. On a color terminal, a pipeline now opens with a full-width band, the only line with a background, holding its position in the run, its name, its short ID and the dry run mode. It closes with a rule spanning the full width in the color of its result, so a failure leaves a red line across the screen. In plain text, the box gains the position, such as "[3/12]". The engine now opens a pipeline as well as closing it, since it knows its position. * feat(log): show every step of a run with the same title Actions, Pushing Git changes, Report, Udash, Summary and Version are steps of a run like Prepare, Auto Discovery or Pipeline, but were shown as headings. They now use the same title. Run Summary stays a heading, as part of the Summary step. The summary template no longer adds blank lines around itself, the banners own the spacing. * feat(log): show the full pipeline ID in a taller pipeline band The band shortened the pipeline ID to 8 characters, but --pipeline-ids only matches complete IDs and a custom pipelineid can be cut in the middle of a meaningful name. On a color terminal, the full ID wasn't printed anywhere anymore. The band becomes a block of colored rows: an empty row, the position, name and dry run mode, the full pipeline ID, and an empty row. Its height makes pipelines even easier to spot when scrolling. On a narrow terminal the "Pipeline ID:" label is dropped so the ID still fits; the band only grows past the terminal width when the ID alone doesn't fit. Plain text is unchanged, it already shows the full ID. * fix(log): size rules by display width Rules under titles, headings and sections were sized by the byte length of the text in plain output and by rune count in styled output, so they misaligned for names with accents, emoji or wide characters. Both renderers now use the displayed width. Also document that captures are global: a resource capture receives the lines of the resources running at the same time. Signed-off-by: Olblak <me@olblak.com> * fix(scm): use the updatecli HTTP client without the cache for SCM APIs The HTTP cache keys GET responses by URL only and no write invalidates them, so an SCM client reading again after creating a pull request, branch, tag or release in the same run could get the state from before the write. Gitea, Bitbucket and Stash clients went through that cache. httpclient gains NewUncachedRetryClient and UncachedTransport, with the same retry, proxy and tracing as NewRetryClient but never the cache, and UserAgent to share the updatecli User-Agent. Gitea, Bitbucket, Stash and GitHub clients now use it. GitHub only sends GraphQL POST requests, so its behaviour doesn't change. The GitLab client used its own HTTP client so far. It now uses the uncached updatecli one, with its own retries disabled to avoid stacking both, and sends the updatecli User-Agent instead of go-gitlab. Signed-off-by: Olblak <me@olblak.com> --------- Signed-off-by: Olblak <me@olblak.com>
1 parent a5c7496 commit 4e87a4a

447 files changed

Lines changed: 3880 additions & 2636 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.golangci.yml‎

Lines changed: 18 additions & 0 deletions

‎cmd/apply.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package cmd
33
import (
44
"os"
55

6-
"github.com/sirupsen/logrus"
76
"github.com/updatecli/updatecli/pkg/core/config"
87
"github.com/updatecli/updatecli/pkg/core/engine/manifest"
8+
"github.com/updatecli/updatecli/pkg/core/log"
99

1010
"github.com/spf13/cobra"
1111
)
@@ -32,7 +32,7 @@ var (
3232
policyReferences = args
3333
err := getPolicyFilesFromRegistry()
3434
if err != nil {
35-
logrus.Errorf("command failed: %s", err)
35+
log.Errorf("command failed: %s", err)
3636
os.Exit(1)
3737
}
3838

@@ -53,11 +53,11 @@ var (
5353
e.Options.Config.ValidateSchema = validateSchema
5454
e.Options.ManifestOptions.RelativePaths = config.RelativePathBase(relativePaths)
5555

56-
logrus.Warningln("Deprecated command, please instead use `updatecli pipeline apply`")
56+
log.Warningln("Deprecated command, please instead use `updatecli pipeline apply`")
5757

5858
err = run("apply")
5959
if err != nil {
60-
logrus.Errorf("command failed: %s", err)
60+
log.Errorf("command failed: %s", err)
6161
os.Exit(1)
6262
}
6363
},

‎cmd/compose_apply.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package cmd
33
import (
44
"os"
55

6-
"github.com/sirupsen/logrus"
76
"github.com/spf13/cobra"
87
"github.com/updatecli/updatecli/pkg/core/compose"
98
"github.com/updatecli/updatecli/pkg/core/config"
109
"github.com/updatecli/updatecli/pkg/core/engine/manifest"
10+
"github.com/updatecli/updatecli/pkg/core/log"
1111
)
1212

1313
var (
@@ -26,7 +26,7 @@ var (
2626

2727
composeFiles, err := compose.New(composeCmdFile, map[string]bool{})
2828
if err != nil {
29-
logrus.Errorf("command failed: %s", err)
29+
log.Errorf("command failed: %s", err)
3030
os.Exit(1)
3131
}
3232

@@ -39,7 +39,7 @@ var (
3939
parseParametersList(composeApplyIgnoredPolicyIDs),
4040
)
4141
if err != nil {
42-
logrus.Errorf("command failed: %s", err)
42+
log.Errorf("command failed: %s", err)
4343
os.Exit(1)
4444
}
4545

@@ -61,7 +61,7 @@ var (
6161

6262
err = run("compose/apply")
6363
if err != nil {
64-
logrus.Errorf("command failed: %s", err)
64+
log.Errorf("command failed: %s", err)
6565
os.Exit(1)
6666
}
6767
},

‎cmd/compose_diff.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package cmd
33
import (
44
"os"
55

6-
"github.com/sirupsen/logrus"
76
"github.com/spf13/cobra"
87
"github.com/updatecli/updatecli/pkg/core/compose"
98
"github.com/updatecli/updatecli/pkg/core/config"
109
"github.com/updatecli/updatecli/pkg/core/engine/manifest"
10+
"github.com/updatecli/updatecli/pkg/core/log"
1111
)
1212

1313
var (
@@ -21,7 +21,7 @@ var (
2121

2222
composeFiles, err := compose.New(composeCmdFile, map[string]bool{})
2323
if err != nil {
24-
logrus.Errorf("command failed: %s", err)
24+
log.Errorf("command failed: %s", err)
2525
os.Exit(1)
2626
}
2727

@@ -34,7 +34,7 @@ var (
3434
parseParametersList(composeDiffIgnoredPolicyIDs),
3535
)
3636
if err != nil {
37-
logrus.Errorf("command failed: %s", err)
37+
log.Errorf("command failed: %s", err)
3838
os.Exit(1)
3939
}
4040

@@ -54,7 +54,7 @@ var (
5454

5555
err = run("compose/diff")
5656
if err != nil {
57-
logrus.Errorf("command failed: %s", err)
57+
log.Errorf("command failed: %s", err)
5858
os.Exit(1)
5959
}
6060
},

‎cmd/compose_show.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package cmd
33
import (
44
"os"
55

6-
"github.com/sirupsen/logrus"
76
"github.com/spf13/cobra"
87
"github.com/updatecli/updatecli/pkg/core/compose"
98
"github.com/updatecli/updatecli/pkg/core/config"
109
"github.com/updatecli/updatecli/pkg/core/engine/manifest"
10+
"github.com/updatecli/updatecli/pkg/core/log"
1111
)
1212

1313
var (
@@ -21,7 +21,7 @@ var (
2121

2222
composeFiles, err := compose.New(composeCmdFile, map[string]bool{})
2323
if err != nil {
24-
logrus.Errorf("command failed: %s", err)
24+
log.Errorf("command failed: %s", err)
2525
os.Exit(1)
2626
}
2727

@@ -34,7 +34,7 @@ var (
3434
parseParametersList(composeShowIgnoredPolicyIDs),
3535
)
3636
if err != nil {
37-
logrus.Errorf("command failed: %s", err)
37+
log.Errorf("command failed: %s", err)
3838
os.Exit(1)
3939
}
4040

@@ -52,7 +52,7 @@ var (
5252
e.Options.ManifestOptions.RelativePaths = config.RelativePathBase(relativePaths)
5353
err = run("compose/show")
5454
if err != nil {
55-
logrus.Errorf("command failed: %s", err)
55+
log.Errorf("command failed: %s", err)
5656
os.Exit(1)
5757
}
5858
},

‎cmd/diff.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package cmd
33
import (
44
"os"
55

6-
"github.com/sirupsen/logrus"
76
"github.com/updatecli/updatecli/pkg/core/config"
87
"github.com/updatecli/updatecli/pkg/core/engine/manifest"
8+
"github.com/updatecli/updatecli/pkg/core/log"
99

1010
"github.com/spf13/cobra"
1111
)
@@ -21,7 +21,7 @@ var (
2121
policyReferences = args
2222
err := getPolicyFilesFromRegistry()
2323
if err != nil {
24-
logrus.Errorf("command failed: %s", err)
24+
log.Errorf("command failed: %s", err)
2525
os.Exit(1)
2626
}
2727

@@ -40,11 +40,11 @@ var (
4040
e.Options.Config.ValidateSchema = validateSchema
4141
e.Options.ManifestOptions.RelativePaths = config.RelativePathBase(relativePaths)
4242

43-
logrus.Warningln("Deprecated command, please instead use `updatecli pipeline diff`")
43+
log.Warningln("Deprecated command, please instead use `updatecli pipeline diff`")
4444

4545
err = run("diff")
4646
if err != nil {
47-
logrus.Errorf("command failed: %s", err)
47+
log.Errorf("command failed: %s", err)
4848
os.Exit(1)
4949
}
5050
},

‎cmd/docs.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99
"path/filepath"
1010
"strings"
1111

12-
"github.com/sirupsen/logrus"
1312
"github.com/spf13/cobra"
1413
"github.com/spf13/cobra/doc"
14+
"github.com/updatecli/updatecli/pkg/core/log"
1515
)
1616

1717
// The docs command is a hidden sub-command that generates the documentation for www.updatecli.io
@@ -71,12 +71,12 @@ var (
7171

7272
for path, hash := range after {
7373
if prev, exists := before[path]; !exists || prev != hash {
74-
logrus.Infof("Documentation files updated in %q", docsDir)
74+
log.Infof("Documentation files updated in %q", docsDir)
7575
os.Exit(2)
7676
}
7777
}
7878

79-
logrus.Infof("Documentation files already up to date in %q", docsDir)
79+
log.Infof("Documentation files already up to date in %q", docsDir)
8080
return nil
8181
},
8282
}

‎cmd/env.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"strconv"
66
"strings"
77

8-
"github.com/sirupsen/logrus"
8+
"github.com/updatecli/updatecli/pkg/core/log"
99
)
1010

1111
const DisableChangelogEnvVar = "UPDATECLI_DISABLE_CHANGELOG"
@@ -26,7 +26,7 @@ func getEnvBoolOrDefault(envVar string, defaultValue bool) bool {
2626

2727
parsed, err := strconv.ParseBool(strings.TrimSpace(value))
2828
if err != nil {
29-
logrus.Debugf(
29+
log.Debugf(
3030
"invalid boolean value for environment variable %q: %q, defaulting to %t",
3131
envVar,
3232
value,

‎cmd/jsonschema.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package cmd
33
import (
44
"os"
55

6-
"github.com/sirupsen/logrus"
76
"github.com/spf13/cobra"
7+
"github.com/updatecli/updatecli/pkg/core/log"
88
)
99

1010
const (
@@ -20,7 +20,7 @@ var (
2020
Run: func(cmd *cobra.Command, args []string) {
2121
err := run(jsonschemaCmdName)
2222
if err != nil {
23-
logrus.Errorf("command failed")
23+
log.Errorf("command failed")
2424
os.Exit(1)
2525
}
2626
},

‎cmd/manifest_init.go‎

Lines changed: 2 additions & 2 deletions

0 commit comments

Comments
 (0)