Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package renovate-pretty-log for
openSUSE:Factory checked in at 2026-03-23 17:13:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/renovate-pretty-log (Old)
and /work/SRC/openSUSE:Factory/.renovate-pretty-log.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "renovate-pretty-log"
Mon Mar 23 17:13:29 2026 rev:2 rq:1341913 version:0.5.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/renovate-pretty-log/renovate-pretty-log.changes
2025-06-13 18:47:32.651984906 +0200
+++
/work/SRC/openSUSE:Factory/.renovate-pretty-log.new.8177/renovate-pretty-log.changes
2026-03-23 17:14:37.504810874 +0100
@@ -1,0 +2,64 @@
+Mon Mar 09 07:53:12 UTC 2026 - Johannes Kastl
<[email protected]>
+
+- Update to version 0.5.2:
+ * Bug Fixes
+ - f67ff8d1 fix(tui): don't show "More ..." indicator when at
+ bottom
+- Update to version 0.5.2:
+ * Bug Fixes
+ - de60a406 fix(tui): don't add an additional line when
+ displaying "More ..." indicator
+- Update to version 0.5.0:
+ * Features
+ - 9716e18e feat(tui): default to debug logs
+- Update to version 0.4.0:
+ * Features
+ - 18aafdb2 feat: add tool to convert from text-based logs to
+ JSONL
+- Update to version 0.3.0:
+ * Features
+ - 8141d99f feat(tui): add search interface
+ * Others
+ - 49984a69 fixup! chore(deps): upgrade to Chroma v2
+- Update to version 0.2.0:
+ * Features
+ - 2f8559ff feat(styles): always pretty-print fields with Chroma
+ - 39755f6f feat(styles): use Srcery colour scheme for JSON
+ - 78aacf91 feat(tui): colour code JSON
+ - 0a6a286b feat(tui): rewrite UI to be split-pane
+ - 4da6971e feat: allow specifying -max-line-size
+ * Bug Fixes
+ - dffdc26d fix(styles): don't re-use bytes.Buffer in the loop
+ - b843db7c fix(tui): log when failing to read log file
+ - 8e140801 fix(tui): parse log lines up to 10MB
+ * Others
+ - 67cdc2e9 Merge branch 'rewrite' into 'main'
+ - 045ee925 chore(deps): upgrade to Chroma v2
+
+-------------------------------------------------------------------
+Thu Mar 05 10:24:01 UTC 2026 - Johannes Kastl
<[email protected]>
+
+- Update to version 0.1.6:
+ * ci: use `CI_JOB_TOKEN` for publishing
+
+-------------------------------------------------------------------
+Thu Mar 05 10:18:51 UTC 2026 - Johannes Kastl
<[email protected]>
+
+- Update to version 0.1.5:
+ * ci: override `goreleaser` entrypoint
+
+-------------------------------------------------------------------
+Thu Mar 05 10:15:37 UTC 2026 - Johannes Kastl
<[email protected]>
+
+- Update to version 0.1.4:
+ * ci: correct Docker image path
+ * docs: add Claude usage
+
+-------------------------------------------------------------------
+Thu Mar 05 10:13:30 UTC 2026 - Johannes Kastl
<[email protected]>
+
+- Update to version 0.1.3:
+ * ci: add Goreleaser setup
+ * Add renovate.json
+
+-------------------------------------------------------------------
Old:
----
renovate-pretty-log-0.1.2.obscpio
New:
----
renovate-pretty-log-0.5.2.obscpio
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ renovate-pretty-log.spec ++++++
--- /var/tmp/diff_new_pack.c6zJW8/_old 2026-03-23 17:14:38.752862780 +0100
+++ /var/tmp/diff_new_pack.c6zJW8/_new 2026-03-23 17:14:38.752862780 +0100
@@ -17,7 +17,7 @@
Name: renovate-pretty-log
-Version: 0.1.2
+Version: 0.5.2
Release: 0
Summary: Two utilities for exploring Renovate debug log files
License: Apache-2.0
++++++ _service ++++++
--- /var/tmp/diff_new_pack.c6zJW8/_old 2026-03-23 17:14:38.796864610 +0100
+++ /var/tmp/diff_new_pack.c6zJW8/_new 2026-03-23 17:14:38.800864777 +0100
@@ -3,7 +3,7 @@
<param
name="url">https://gitlab.com/tanna.dev/renovate-pretty-log.git</param>
<param name="scm">git</param>
<param name="exclude">.git</param>
- <param name="revision">refs/tags/v0.1.2</param>
+ <param name="revision">refs/tags/v0.5.2</param>
<param name="versionformat">@PARENT_TAG@</param>
<param name="versionrewrite-pattern">v(.*)</param>
<param name="changesgenerate">enable</param>
++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.c6zJW8/_old 2026-03-23 17:14:38.824865775 +0100
+++ /var/tmp/diff_new_pack.c6zJW8/_new 2026-03-23 17:14:38.828865941 +0100
@@ -1,6 +1,6 @@
<servicedata>
<service name="tar_scm">
<param
name="url">https://gitlab.com/tanna.dev/renovate-pretty-log.git</param>
- <param
name="changesrevision">c29241031096ff5d3f36a751167f88440a79eae5</param></service></servicedata>
+ <param
name="changesrevision">f67ff8d1e2034f80b0b5b2b8be58d15438388c2e</param></service></servicedata>
(No newline at EOF)
++++++ renovate-pretty-log-0.1.2.obscpio -> renovate-pretty-log-0.5.2.obscpio
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/.goreleaser.yaml
new/renovate-pretty-log-0.5.2/.goreleaser.yaml
--- old/renovate-pretty-log-0.1.2/.goreleaser.yaml 1970-01-01
01:00:00.000000000 +0100
+++ new/renovate-pretty-log-0.5.2/.goreleaser.yaml 2026-03-06
16:03:10.000000000 +0100
@@ -0,0 +1,121 @@
+version: 2
+
+before:
+ hooks:
+ - go mod tidy
+
+builds:
+ - id: renovate-pretty-log
+ main: ./cmd/renovate-pretty-log
+ binary: renovate-pretty-log
+ flags:
+ - -trimpath
+ env:
+ - CGO_ENABLED=0
+ goos:
+ - linux
+ - darwin
+ - windows
+ goarch:
+ - amd64
+ - arm64
+ ignore:
+ - goarch: "386"
+ goos: windows
+ ldflags:
+ - -s -w
+ - -X main.version={{.Version}}
+ - -X main.commit={{.Commit}}
+ - -X main.date={{.Date}}
+
+ - id: renovate-pretty-log-tui
+ main: ./cmd/renovate-pretty-log-tui
+ binary: renovate-pretty-log-tui
+ flags:
+ - -trimpath
+ env:
+ - CGO_ENABLED=0
+ goos:
+ - linux
+ - darwin
+ - windows
+ goarch:
+ - amd64
+ - arm64
+ ignore:
+ - goarch: "386"
+ goos: windows
+ ldflags:
+ - -s -w
+ - -X main.version={{.Version}}
+ - -X main.commit={{.Commit}}
+ - -X main.date={{.Date}}
+
+ - id: renovate-text-to-json
+ main: ./cmd/renovate-text-to-json
+ binary: renovate-text-to-json
+ flags:
+ - -trimpath
+ env:
+ - CGO_ENABLED=0
+ goos:
+ - linux
+ - darwin
+ - windows
+ goarch:
+ - amd64
+ - arm64
+ ignore:
+ - goarch: "386"
+ goos: windows
+ ldflags:
+ - -s -w
+ - -X main.version={{.Version}}
+ - -X main.commit={{.Commit}}
+ - -X main.date={{.Date}}
+
+archives:
+ - id: no-version
+ formats:
+ - zip
+ name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
+ wrap_in_directory: false
+ files:
+ - LICENSE
+ - README.md
+
+checksum:
+ name_template: 'checksums.txt'
+
+snapshot:
+ version_template: "{{ incpatch .Version }}-next"
+
+changelog:
+ sort: asc
+ filters:
+ exclude:
+ - '^docs:'
+ - '^test:'
+ - '^ci:'
+ - '^chore:'
+ - 'README'
+ groups:
+ - title: Features
+ regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
+ order: 0
+ - title: Bug Fixes
+ regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
+ order: 1
+ - title: Others
+ order: 999
+
+gitlab_urls:
+ api: https://gitlab.com/api/v4/
+ download: https://gitlab.com
+ use_package_registry: true
+ use_job_token: true
+
+release:
+ gitlab:
+ owner: tanna.dev
+ name: renovate-pretty-log
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/README.md
new/renovate-pretty-log-0.5.2/README.md
--- old/renovate-pretty-log-0.1.2/README.md 2025-05-23 18:08:47.000000000
+0200
+++ new/renovate-pretty-log-0.5.2/README.md 2026-03-06 16:03:10.000000000
+0100
@@ -1,6 +1,12 @@
# `renovate-pretty-log`
-Two utilities for exploring [Renovate](https://docs.renovatebot.com/) debug
log files.
+Three utilities for exploring [Renovate](https://docs.renovatebot.com/) debug
log files.
+
+## Installing
+
+There are pre-built binaries [attached to the GitLab
Releases](https://gitlab.com/tanna.dev/renovate-pretty-log/-/releases/), which
contains `renovate-pretty-log`, `renovate-pretty-log-tui`, and
`renovate-text-to-json`.
+
+You can also see these [in the GitLab Package
Registry](https://gitlab.com/tanna.dev/renovate-pretty-log/-/packages).
## `cmd/renovate-pretty-log-tui`
@@ -44,6 +50,16 @@
renovate-pretty-log -path /path/to/debug.log -level debug
```
+## `cmd/renovate-text-to-json`
+
+Given a plain-text "pretty" log output from Renovate, convert it to JSONL
format for `renovate-pretty-log` or `renovate-pretty-log-tui`.
+
+This can also allow usage such as:
+
+```sh
+renovate-text-to-json < log-lines.txt | renovate-pretty-log-tui -path
/dev/stdin
+```
+
## License
This project is licensed under the Apache-2.0 license.
@@ -54,3 +70,4 @@
- claude:3.7-sonnet-thinking
- gpt:4o
- gpt:4.1
+- Claude Sonnet 4.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/renovate-pretty-log-0.1.2/cmd/renovate-pretty-log/main.go
new/renovate-pretty-log-0.5.2/cmd/renovate-pretty-log/main.go
--- old/renovate-pretty-log-0.1.2/cmd/renovate-pretty-log/main.go
2025-05-23 18:08:47.000000000 +0200
+++ new/renovate-pretty-log-0.5.2/cmd/renovate-pretty-log/main.go
2026-03-06 16:03:10.000000000 +0100
@@ -44,6 +44,7 @@
levelFlag := flag.String("level", "info", "Set the log level (trace,
debug, info, warn, error, fatal)")
pathFlag := flag.String("path", "", "The path to the debug log file
(`RENOVATE_DEBUG_LOG_FILE`)")
+ maxLineSizeMB := flag.Int("max-line-size", 10, "Maximum line size in MB
(default: 10)")
flag.Parse()
if pathFlag == nil {
@@ -65,6 +66,10 @@
defer f.Close()
scanner := bufio.NewScanner(f)
+ // via https://stackoverflow.com/a/37455465, make sure we can handle
lines up to configured size
+ buf := make([]byte, 0, bufio.MaxScanTokenSize)
+ maxBytes := *maxLineSizeMB * 1024 * 1024
+ scanner.Buffer(buf, maxBytes)
for scanner.Scan() {
var line log.Line
data := scanner.Bytes()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/renovate-pretty-log-0.1.2/cmd/renovate-pretty-log-tui/main.go
new/renovate-pretty-log-0.5.2/cmd/renovate-pretty-log-tui/main.go
--- old/renovate-pretty-log-0.1.2/cmd/renovate-pretty-log-tui/main.go
2025-05-23 18:08:47.000000000 +0200
+++ new/renovate-pretty-log-0.5.2/cmd/renovate-pretty-log-tui/main.go
2026-03-06 16:03:10.000000000 +0100
@@ -8,36 +8,45 @@
"os"
"strings"
+ "github.com/charmbracelet/bubbles/textinput"
+ "github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/lipgloss"
charmlog "github.com/charmbracelet/log"
"gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
"gitlab.com/tanna.dev/renovate-pretty-log/internal/style"
)
-// parentViewportHeight indicates how many lines of logs will be shown on a
given "page" of logs
-const parentViewportHeight = 30
-
-// extrasViewportHeight indicates how many lines of "extra" metadata will be
shown under a given log, if expanded
-const extrasViewportHeight = 20
-
type model struct {
- // logs contains the log lines themselves. They must be pre-filtered
- logs []log.Line
- cursor int
- // parentViewportStart is the index of the first visible log
- parentViewportStart int
- // expanded tracks which indexes are currently expanded
- expanded map[int]bool
- // extrasExpandedViewport allows expanding a given log.Line's Extras,
to show any additional context the log line has
- extrasExpandedViewport extrasExpandedViewport
+ logs []log.Line
+ filteredLogs []int // indices into logs array
+ cursor int
+ logsViewport viewport.Model
+ extrasViewport viewport.Model
+ ready bool
+ width int
+ height int
+ searchMode bool
+ searchInput textinput.Model
+ searchQuery string
}
func newModel(lines []log.Line) model {
+ ti := textinput.New()
+ ti.Placeholder = "Search log messages..."
+ ti.CharLimit = 100
+
+ // Initialize filteredLogs with all indices
+ filteredLogs := make([]int, len(lines))
+ for i := range lines {
+ filteredLogs[i] = i
+ }
+
return model{
- logs: lines,
- cursor: 0,
- parentViewportStart: 0,
- expanded: make(map[int]bool),
+ logs: lines,
+ filteredLogs: filteredLogs,
+ cursor: 0,
+ searchInput: ti,
}
}
@@ -47,278 +56,296 @@
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
- case tea.KeyMsg:
- switch msg.String() {
- case
- "up",
- "k":
- m.MoveUp()
- case
- "down",
- "j":
- m.MoveDown()
- case
- "ctrl+u",
- "pgup":
- m.cursor -= parentViewportHeight
- if m.cursor < 0 {
- m.cursor = 0
- }
- m.parentViewportStart -= parentViewportHeight
- if m.parentViewportStart < 0 {
- m.parentViewportStart = 0
- }
-
- case
- "ctrl+d",
- "pgdown":
- m.cursor += parentViewportHeight
+ case tea.WindowSizeMsg:
+ m.width = msg.Width
+ m.height = msg.Height
+
+ footerHeight := 1
+ searchBarHeight := 0
+ if m.searchMode {
+ searchBarHeight = 1
+ }
+ availableHeight := m.height - footerHeight - searchBarHeight
+ contentHeight := availableHeight
+
+ leftPaneWidth := (m.width - 1) / 2 // -1 for divider
+ rightPaneWidth := m.width - leftPaneWidth - 1
+
+ if !m.ready {
+ m.logsViewport = viewport.New(leftPaneWidth,
contentHeight)
+ m.extrasViewport = viewport.New(rightPaneWidth,
contentHeight)
+ m.logsViewport.YPosition = 0
+ m.extrasViewport.YPosition = 0
+ // Enable scroll indicators for extras viewport
+ m.extrasViewport.Style = lipgloss.NewStyle()
+ m.ready = true
+ m.updateLogsViewport()
+ m.updateExtrasViewport()
+ } else {
+ m.logsViewport.Width = leftPaneWidth
+ m.logsViewport.Height = contentHeight
+ m.extrasViewport.Width = rightPaneWidth
+ m.extrasViewport.Height = contentHeight
+ m.updateLogsViewport()
+ m.updateExtrasViewport()
+ }
- if m.cursor >= len(m.logs)-1 {
- m.cursor = len(m.logs) - 1
- }
+ return m, nil
- m.parentViewportStart += parentViewportHeight
- if m.parentViewportStart >= len(m.logs)-1 {
- m.parentViewportStart = len(m.logs) - 1
+ case tea.KeyMsg:
+ // If in search mode, handle text input
+ if m.searchMode {
+ switch msg.String() {
+ case "enter":
+ m.searchMode = false
+ m.searchInput.Blur()
+ m.searchQuery = m.searchInput.Value()
+ m.filterLogs()
+ return m, nil
+ case "esc":
+ m.searchMode = false
+ m.searchInput.Blur()
+ // Reset search if ESC pressed
+ m.searchInput.SetValue("")
+ m.searchQuery = ""
+ m.filterLogs()
+ return m, nil
+ default:
+ var cmd tea.Cmd
+ m.searchInput, cmd = m.searchInput.Update(msg)
+ return m, cmd
}
- case "enter":
- m.expanded[m.cursor] = !m.expanded[m.cursor]
+ }
- log := m.logs[m.cursor]
- extras := strings.Split(style.RenderLineExtras(log),
"\n")
- m.extrasExpandedViewport.cursor = 0
- m.extrasExpandedViewport.length = len(extras)
- case "q", "esc", "ctrl+c":
+ // Normal mode key handling
+ switch msg.String() {
+ case "up", "k":
+ if m.cursor > 0 {
+ m.cursor--
+ m.updateLogsViewport()
+ m.updateExtrasViewport()
+ }
+ case "down", "j":
+ if m.cursor < len(m.filteredLogs)-1 {
+ m.cursor++
+ m.updateLogsViewport()
+ m.updateExtrasViewport()
+ }
+ case "ctrl+u", "pgup":
+ pageSize := m.logsViewport.Height
+ m.cursor = max(0, m.cursor-pageSize)
+ m.updateLogsViewport()
+ m.updateExtrasViewport()
+ case "ctrl+d", "pgdown":
+ pageSize := m.logsViewport.Height
+ m.cursor = min(len(m.filteredLogs)-1, m.cursor+pageSize)
+ m.updateLogsViewport()
+ m.updateExtrasViewport()
+ case "left", "h":
+ m.extrasViewport.LineUp(1)
+ return m, nil
+ case "right", "l":
+ m.extrasViewport.LineDown(1)
+ return m, nil
+ case "/":
+ m.searchMode = true
+ m.searchInput.Focus()
+ return m, nil
+ case "q", "ctrl+c":
return m, tea.Quit
+ case "esc":
+ // Clear search with ESC in normal mode
+ if m.searchQuery != "" {
+ m.searchInput.SetValue("")
+ m.searchQuery = ""
+ m.filterLogs()
+ }
}
}
return m, nil
}
-func (m model) View() string {
- var b strings.Builder
-
- end := min(m.parentViewportStart+parentViewportHeight, len(m.logs))
-
- for i := m.parentViewportStart; i < end; i++ {
- line := m.logs[i]
+func (m *model) updateLogsViewport() {
+ if !m.ready {
+ return
+ }
+ var lines []string
+ for i, logIdx := range m.filteredLogs {
+ line := m.logs[logIdx]
cursor := " "
if i == m.cursor {
cursor = ">"
}
- hasExtras := len(line.Extras) > 0
- hasExtrasIndicator := " "
- if hasExtras {
- hasExtrasIndicator =
style.LogLineHasExtrasStyle.Render("+")
- if m.expanded[i] {
- hasExtrasIndicator =
style.LogLineHasExtrasStyle.Render("-")
- }
+ hasExtras := " "
+ if len(line.Extras) > 0 {
+ hasExtras = style.LogLineHasExtrasStyle.Render("+")
}
renderedLine := style.RenderLine(line, false)
-
- fmt.Fprintf(&b, "%s %s %s", cursor, hasExtrasIndicator,
renderedLine)
- if m.expanded[i] && hasExtras {
- extras := strings.Split(style.RenderLineExtras(line),
"\n")
-
- tooLongPrefix := ""
- tooLongSuffix := ""
-
- start := 0
- end := extrasViewportHeight
- if end > len(extras) {
- end = len(extras)
- }
-
- if len(extras) > extrasViewportHeight {
- tooLongSuffix = "\n" + style.LogLineExtraIndent
+ "..."
- }
-
- if i == m.cursor {
- if m.extrasExpandedViewport.AtTop() {
- tooLongPrefix = ""
- } else {
- tooLongPrefix = fmt.Sprintf("%s...
%d/%d\n", style.LogLineExtraIndent, m.extrasExpandedViewport.cursor,
m.extrasExpandedViewport.length)
- }
-
- if m.extrasExpandedViewport.AtBottom() {
- tooLongSuffix = ""
- } else {
- tooLongSuffix = fmt.Sprintf("\n%s...
%d/%d", style.LogLineExtraIndent,
m.extrasExpandedViewport.cursor+extrasViewportHeight,
m.extrasExpandedViewport.length)
- }
-
- start = m.extrasExpandedViewport.cursor
- end = m.extrasExpandedViewport.cursor +
extrasViewportHeight
- if end > len(extras) {
- end = len(extras)
- }
- }
-
- fmt.Fprintf(&b, "\n%s%s%s",
- tooLongPrefix,
-
style.LogLineExtraValueStyle.Render(strings.Join(extras[start:end], "\n")),
- tooLongSuffix)
- }
- fmt.Fprintln(&b)
+ lines = append(lines, fmt.Sprintf("%s %s %s", cursor,
hasExtras, renderedLine))
}
- if len(m.logs) > end {
- fmt.Fprintf(&b, "\nViewing %d/%d logs\n", m.cursor,
len(m.logs)-end)
- }
+ m.logsViewport.SetContent(strings.Join(lines, "\n"))
- // TODO: migrate this to a help widget
- currentLog := m.logs[m.cursor]
- if len(currentLog.Extras) > 0 {
- if m.expanded[m.cursor] {
- fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q
to quit]", style.LogLineHasExtrasStyle.Render("Enter to collapse"))
- } else {
- fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q
to quit]", style.LogLineHasExtrasStyle.Render("Enter to expand"))
- }
- } else {
- fmt.Fprintf(&b, "\n[Use arrow keys to navigate, %s, q to
quit]", style.LogLineTimestampStyle.Render("Enter to expand"))
+ // Calculate line position and scroll to make cursor visible
+ cursorLine := m.cursor
+ if cursorLine < m.logsViewport.YOffset {
+ m.logsViewport.YOffset = cursorLine
+ } else if cursorLine >= m.logsViewport.YOffset+m.logsViewport.Height {
+ m.logsViewport.YOffset = cursorLine - m.logsViewport.Height + 1
}
-
- return b.String()
}
-func (m *model) MoveUp() {
- // if we're currently at an expanded log line ...
- if m.expanded[m.cursor] {
- // and we're already at the top
- if m.extrasExpandedViewport.AtTop() {
- // then scroll up as normal, out of the expanded log
line
- m.cursor--
- if m.cursor < 0 {
- m.cursor = 0
- }
-
- // if we've gone out of view (as we're now before the
previous start point) scroll us up
- if m.cursor < m.parentViewportStart {
- m.parentViewportStart--
- }
- if m.parentViewportStart < 0 {
- m.parentViewportStart = 0
- }
-
- // we may now be on a log line that is expanded, so we
need to makes sure our internal state is reset
- m.updateExtrasExpandedViewportStateAfterMoving()
- } else {
- // otherwise, we want to scroll our expanded log line
up a line
- m.extrasExpandedViewport.MoveUp()
- }
- } else if m.cursor > 0 {
- // if we're on a regular line, then move us up
- m.cursor--
- if m.cursor < 0 {
- m.cursor = 0
- }
-
- // if we've gone out of view (as we're now before the previous
start point) scroll us up
- if m.cursor < m.parentViewportStart {
- m.parentViewportStart--
- }
- if m.parentViewportStart < 0 {
- m.parentViewportStart = 0
- }
-
- previous := m.cursor - 1
- if previous < 0 {
- previous = 0
- }
-
- // we may now be on a log line that is expanded, so we need to
makes sure our internal state is reset
- m.updateExtrasExpandedViewportStateAfterMoving()
+func (m *model) updateExtrasViewport() {
+ if !m.ready || m.cursor >= len(m.filteredLogs) {
+ return
}
-}
-func (m *model) MoveDown() {
- // if we're currently at an expanded log line ...
- if m.expanded[m.cursor] {
- // and we're already at the bottom
- if m.extrasExpandedViewport.AtBottom() {
- // then scroll down as normal, out of the expanded log
line
- m.cursor++
- if m.cursor > len(m.logs)-1 {
- m.cursor = len(m.logs) - 1
- }
-
- // if we've gone out of view (as we're now before the
previous start point) scroll us down
- if m.cursor > m.parentViewportStart {
- m.parentViewportStart++
- }
- if m.parentViewportStart > len(m.logs)-1 {
- m.parentViewportStart = len(m.logs) - 1
- }
-
- // we may now be on a log line that is expanded, so we
need to makes sure our internal state is reset
- m.updateExtrasExpandedViewportStateAfterMoving()
- } else {
- // otherwise, we want to scroll our expanded log line
down a line
- m.extrasExpandedViewport.MoveDown()
- }
- } else if m.cursor < len(m.logs)-1 {
- // if we're on a regular line, then move us down
- m.cursor++
- if m.cursor > len(m.logs)-1 {
- m.cursor = len(m.logs) - 1
- }
+ logIdx := m.filteredLogs[m.cursor]
+ currentLog := m.logs[logIdx]
- // if we've gone out of view (as we're now before the previous
start point) scroll us down
- if m.cursor > m.parentViewportStart {
- m.parentViewportStart++
+ if len(currentLog.Extras) == 0 {
+ emptyMsg := lipgloss.NewStyle().
+ Foreground(lipgloss.Color("240")).
+ Italic(true).
+ Render("No additional context for this log")
+ m.extrasViewport.SetContent(emptyMsg)
+ } else {
+ extrasText := style.RenderLineExtras(currentLog)
+ // Trim any trailing newlines that might cause height mismatches
+ extrasText = strings.TrimRight(extrasText, "\n")
+ m.extrasViewport.SetContent(extrasText)
+ }
+
+ // Reset scroll position when changing logs
+ m.extrasViewport.YOffset = 0
+}
+
+func (m *model) filterLogs() {
+ if m.searchQuery == "" {
+ // No search - show all logs
+ m.filteredLogs = make([]int, len(m.logs))
+ for i := range m.logs {
+ m.filteredLogs[i] = i
}
- if m.parentViewportStart > len(m.logs)-1 {
- m.parentViewportStart = len(m.logs) - 1
+ } else {
+ // Filter logs based on search query
+ query := strings.ToLower(m.searchQuery)
+ m.filteredLogs = []int{}
+ for i, line := range m.logs {
+ if strings.Contains(strings.ToLower(line.Message),
query) {
+ m.filteredLogs = append(m.filteredLogs, i)
+ }
}
+ }
- // we may now be on a log line that is expanded, so we need to
makes sure our internal state is reset
- m.updateExtrasExpandedViewportStateAfterMoving()
+ // Reset cursor if it's out of bounds
+ if m.cursor >= len(m.filteredLogs) {
+ m.cursor = max(0, len(m.filteredLogs)-1)
}
-}
-func (m *model) updateExtrasExpandedViewportStateAfterMoving() {
- log := m.logs[m.cursor]
- extras := strings.Split(style.RenderLineExtras(log), "\n")
- m.extrasExpandedViewport.cursor = 0
- m.extrasExpandedViewport.length = len(extras)
+ m.updateLogsViewport()
+ m.updateExtrasViewport()
}
-// extrasExpandedViewport allows expanding a given log line's "extra" log
metadata, in a scrollable fashion.
-type extrasExpandedViewport struct {
- cursor int
- length int
-}
+func (m model) View() string {
+ if !m.ready {
+ return "Loading..."
+ }
-func (e extrasExpandedViewport) AtTop() bool {
- return e.cursor == 0
-}
+ leftPaneWidth := m.logsViewport.Width
+ rightPaneWidth := m.extrasViewport.Width
+ paneHeight := m.logsViewport.Height
+
+ // Create layout with divider - fix height to match viewport
+ leftPaneStyled := lipgloss.NewStyle().
+ BorderStyle(lipgloss.NormalBorder()).
+ BorderRight(true).
+ Width(leftPaneWidth).
+ Height(paneHeight).
+ Align(lipgloss.Left, lipgloss.Top).
+ Render(m.logsViewport.View())
+
+ // Add scroll percentage indicator for extras if there's scrollable
content
+ extrasView := m.extrasViewport.View()
+ if m.extrasViewport.TotalLineCount() > m.extrasViewport.Height {
+ scrollIndicator := ""
+
+ // Show arrow indicators based on position
+ if !m.extrasViewport.AtTop() && !m.extrasViewport.AtBottom() {
+ scrollPercent := int(m.extrasViewport.ScrollPercent() *
100)
+ scrollIndicator = lipgloss.NewStyle().
+ Foreground(lipgloss.Color("240")).
+ Render(fmt.Sprintf(" ↑↓ %d%%", scrollPercent))
+ } else if !m.extrasViewport.AtBottom() {
+ // At top but not at bottom - can scroll down
+ scrollIndicator = lipgloss.NewStyle().
+ Foreground(lipgloss.Color("240")).
+ Render(" ↓ More")
+ } else if !m.extrasViewport.AtTop() {
+ // At bottom but not at top - can scroll up
+ scrollPercent := int(m.extrasViewport.ScrollPercent() *
100)
+ scrollIndicator = lipgloss.NewStyle().
+ Foreground(lipgloss.Color("240")).
+ Render(fmt.Sprintf(" ↑ %d%%", scrollPercent))
+ }
+
+ // Overlay indicator on the last line of the viewport
+ if scrollIndicator != "" {
+ lines := strings.Split(extrasView, "\n")
+ if len(lines) > 0 && !m.extrasViewport.AtBottom() {
+ lines[len(lines)-1] = scrollIndicator
+ extrasView = strings.Join(lines, "\n")
+ }
+ }
+ }
+
+ rightPaneStyled := lipgloss.NewStyle().
+ Width(rightPaneWidth).
+ Height(paneHeight).
+ Align(lipgloss.Left, lipgloss.Top).
+ Render(extrasView)
+
+ content := lipgloss.JoinHorizontal(lipgloss.Top, leftPaneStyled,
rightPaneStyled)
+
+ var parts []string
+
+ // Add search bar if in search mode
+ if m.searchMode {
+ searchBar := lipgloss.NewStyle().
+ Foreground(lipgloss.Color("14")).
+ Render("/") + " " + m.searchInput.View()
+ parts = append(parts, content, searchBar)
+ } else {
+ parts = append(parts, content)
+ }
-func (e extrasExpandedViewport) AtBottom() bool {
- if e.cursor == (e.length - 1) {
- return true
+ // Footer with help
+ helpText := "↑/↓: Navigate | ←/→: Scroll extras | /: Search | q: Quit"
+ if m.searchQuery != "" {
+ helpText = fmt.Sprintf("↑/↓: Navigate | ←/→: Scroll extras | /:
Search | ESC: Clear search (%d/%d matches)", len(m.filteredLogs), len(m.logs))
+ }
+ if len(m.filteredLogs) > 0 {
+ logIdx := m.filteredLogs[m.cursor]
+ currentLog := m.logs[logIdx]
+ if len(currentLog.Extras) == 0 {
+ helpText = "↑/↓: Navigate | /: Search | q: Quit | (No
extras for this log)"
+ }
}
- isShowingLastPage := (e.length)-extrasViewportHeight <= e.cursor
+ footer := lipgloss.NewStyle().
+ Foreground(lipgloss.Color("240")).
+ Render(helpText)
- return isShowingLastPage
-}
+ parts = append(parts, footer)
-func (e *extrasExpandedViewport) MoveUp() {
- e.cursor--
- if e.cursor <= 0 {
- e.cursor = 0
- }
-}
+ // Combine all parts
+ view := lipgloss.JoinVertical(lipgloss.Left, parts...)
-func (e *extrasExpandedViewport) MoveDown() {
- e.cursor++
- if e.cursor > e.length {
- e.cursor = e.length
- }
+ return view
}
func parseLevelFlag(level *string) (log.Level, error) {
@@ -349,8 +376,9 @@
func main() {
cliLogger := charmlog.New(os.Stderr)
- levelFlag := flag.String("level", "info", "Set the log level (trace,
debug, info, warn, error, fatal)")
+ levelFlag := flag.String("level", "debug", "Set the log level (trace,
debug, info, warn, error, fatal)")
pathFlag := flag.String("path", "", "The path to the debug log file
(`RENOVATE_DEBUG_LOG_FILE`)")
+ maxLineSizeMB := flag.Int("max-line-size", 10, "Maximum line size in MB
(default: 10)")
flag.Parse()
if pathFlag == nil {
@@ -378,8 +406,12 @@
}
var lines []log.Line
- // TODO via
https://gitlab.com/jamietanna/dotfiles-arch/-/blob/67d79966/go/home/go/src/jvt.me/dotfiles/jjj/main.go#L24-26
we may want to allow handling /very/ long log lines
+
scanner := bufio.NewScanner(f)
+ // via https://stackoverflow.com/a/37455465, make sure we can handle
lines up to configured size
+ buf := make([]byte, 0, bufio.MaxScanTokenSize)
+ maxBytes := *maxLineSizeMB * 1024 * 1024
+ scanner.Buffer(buf, maxBytes)
for scanner.Scan() {
var line log.Line
data := scanner.Bytes()
@@ -394,12 +426,17 @@
}
}
+ if err := scanner.Err(); err != nil {
+ cliLogger.Fatal(fmt.Sprintf("Failed to parse log file: %v",
err), "err", err)
+ os.Exit(1)
+ }
+
if len(lines) == 0 {
fmt.Printf("The file you provided did not contain any %s level
logs\n", allowedLogLevel.String())
os.Exit(0)
}
- p := tea.NewProgram(newModel(lines))
+ p := tea.NewProgram(newModel(lines), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Error starting app: %v\n", err)
os.Exit(1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/renovate-pretty-log-0.1.2/cmd/renovate-text-to-json/main.go
new/renovate-pretty-log-0.5.2/cmd/renovate-text-to-json/main.go
--- old/renovate-pretty-log-0.1.2/cmd/renovate-text-to-json/main.go
1970-01-01 01:00:00.000000000 +0100
+++ new/renovate-pretty-log-0.5.2/cmd/renovate-text-to-json/main.go
2026-03-06 16:03:10.000000000 +0100
@@ -0,0 +1,216 @@
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "os"
+ "regexp"
+ "strings"
+ "time"
+
+ charmlog "github.com/charmbracelet/log"
+
+ log "gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
+)
+
+var (
+ // Matches "LEVEL: Message" or "LEVEL Message"
+ logLineRegex =
regexp.MustCompile(`^(TRACE|DEBUG|INFO|WARN|ERROR|FATAL):?\s+(.+)$`)
+)
+
+func parseLevel(levelStr string) log.Level {
+ switch strings.ToUpper(levelStr) {
+ case "TRACE":
+ return log.LevelTrace
+ case "DEBUG":
+ return log.LevelDebug
+ case "INFO":
+ return log.LevelInfo
+ case "WARN":
+ return log.LevelWarn
+ case "ERROR":
+ return log.LevelError
+ case "FATAL":
+ return log.LevelFatal
+ default:
+ return log.LevelInfo
+ }
+}
+
+func startsJSON(line string) bool {
+ line = strings.TrimSpace(line)
+ return strings.HasPrefix(line, "{") || strings.HasPrefix(line, "[")
+}
+
+func main() {
+ cliLogger := charmlog.New(os.Stderr)
+
+ scanner := bufio.NewScanner(os.Stdin)
+ buf := make([]byte, 0, bufio.MaxScanTokenSize)
+ maxBytes := 10 * 1024 * 1024 // 10MB
+ scanner.Buffer(buf, maxBytes)
+
+ var currentLine *log.Line
+ var jsonBuffer strings.Builder
+ inJSON := false
+
+ for scanner.Scan() {
+ text := scanner.Text()
+ text = strings.TrimRight(text, "\r\n")
+
+ // Skip empty lines unless we're in JSON
+ if strings.TrimSpace(text) == "" && !inJSON {
+ continue
+ }
+
+ // Check if this is a log line (starts with level)
+ if matches := logLineRegex.FindStringSubmatch(text); matches !=
nil && !inJSON {
+ // If we have a pending line with JSON, output it first
+ if currentLine != nil && jsonBuffer.Len() > 0 {
+ if err := parseAndAddExtras(currentLine,
jsonBuffer.String()); err != nil {
+ cliLogger.Warn("Failed to parse JSON
extras", "err", err, "json", jsonBuffer.String())
+ }
+ outputLine(currentLine)
+ jsonBuffer.Reset()
+ } else if currentLine != nil {
+ // Output line without extras
+ outputLine(currentLine)
+ }
+
+ // Start new line
+ levelStr := matches[1]
+ message := matches[2]
+
+ currentLine = &log.Line{
+ Name: "renovate",
+ Hostname: "unknown",
+ Pid: 0,
+ Level: parseLevel(levelStr),
+ LogContext: "unknown",
+ Repository: "",
+ Message: message,
+ Time: time.Time{},
+ Version: 0,
+ Extras: make(map[string]any),
+ }
+ } else if startsJSON(text) && currentLine != nil && !inJSON {
+ // Start of JSON block
+ inJSON = true
+ jsonBuffer.WriteString(text)
+ jsonBuffer.WriteString("\n")
+ } else if inJSON {
+ // Continue collecting JSON
+ jsonBuffer.WriteString(text)
+ jsonBuffer.WriteString("\n")
+
+ // Try to parse to see if we have complete JSON
+ trimmed := strings.TrimSpace(jsonBuffer.String())
+ var test any
+ if json.Unmarshal([]byte(trimmed), &test) == nil {
+ // Valid JSON, end of block
+ inJSON = false
+ }
+ } else {
+ // Could be continuation of message or unknown format
+ if currentLine != nil && !inJSON {
+ // Append to current message
+ currentLine.Message += " " + text
+ } else if !inJSON {
+ // Treat as INFO line if we don't have context
+ currentLine = &log.Line{
+ Name: "renovate",
+ Hostname: "unknown",
+ Pid: 0,
+ Level: log.LevelInfo,
+ LogContext: "unknown",
+ Repository: "",
+ Message: text,
+ Time: time.Time{}, // Zero time
(0001-01-01T00:00:00Z) as placeholder
+ Version: 0,
+ Extras: make(map[string]any),
+ }
+ }
+ }
+ }
+
+ // Output the last line if there is one
+ if currentLine != nil {
+ if jsonBuffer.Len() > 0 {
+ if err := parseAndAddExtras(currentLine,
jsonBuffer.String()); err != nil {
+ cliLogger.Warn("Failed to parse JSON extras",
"err", err, "json", jsonBuffer.String())
+ }
+ }
+ outputLine(currentLine)
+ }
+
+ if err := scanner.Err(); err != nil {
+ cliLogger.Fatal("Error reading stdin", "err", err)
+ }
+}
+
+func parseAndAddExtras(line *log.Line, jsonStr string) error {
+ jsonStr = strings.TrimSpace(jsonStr)
+
+ var extras map[string]any
+ if err := json.Unmarshal([]byte(jsonStr), &extras); err != nil {
+ return err
+ }
+
+ if line.Extras == nil {
+ line.Extras = make(map[string]any)
+ }
+
+ for k, v := range extras {
+ // Check if it's a known field that should be set on the line
struct
+ if k == "repository" {
+ if repo, ok := v.(string); ok {
+ line.Repository = repo
+ continue
+ }
+ }
+ line.Extras[k] = v
+ }
+
+ return nil
+}
+
+func outputLine(line *log.Line) {
+ // Build JSON manually to maintain field order
+ var sb strings.Builder
+ sb.WriteString("{")
+
+ // Standard fields in order (msg, time, v at the end)
+ fmt.Fprintf(&sb, `"name":%q,`, line.Name)
+ fmt.Fprintf(&sb, `"hostname":%q,`, line.Hostname)
+ fmt.Fprintf(&sb, `"pid":%d,`, line.Pid)
+ fmt.Fprintf(&sb, `"level":%d,`, line.Level)
+ fmt.Fprintf(&sb, `"logContext":%q`, line.LogContext)
+
+ // Only include repository if it's set
+ if line.Repository != "" {
+ sb.WriteString(",")
+ fmt.Fprintf(&sb, `"repository":%q`, line.Repository)
+ }
+
+ // Add extras
+ if len(line.Extras) > 0 {
+ for k, v := range line.Extras {
+ sb.WriteString(",")
+ jsonVal, err := json.Marshal(v)
+ if err != nil {
+ charmlog.New(os.Stderr).Warn("Failed to marshal
extra field", "key", k, "err", err)
+ continue
+ }
+ fmt.Fprintf(&sb, `%q:%s`, k, string(jsonVal))
+ }
+ }
+
+ // msg, time, v always at the end
+ fmt.Fprintf(&sb, `,"msg":%q,`, line.Message)
+ fmt.Fprintf(&sb, `"time":%q,`, line.Time.Format(time.RFC3339))
+ fmt.Fprintf(&sb, `"v":%d`, line.Version)
+
+ sb.WriteString("}")
+ fmt.Println(sb.String())
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/go.mod
new/renovate-pretty-log-0.5.2/go.mod
--- old/renovate-pretty-log-0.1.2/go.mod 2025-05-23 18:08:47.000000000
+0200
+++ new/renovate-pretty-log-0.5.2/go.mod 2026-03-06 16:03:10.000000000
+0100
@@ -1,32 +1,38 @@
module gitlab.com/tanna.dev/renovate-pretty-log
-go 1.24
+go 1.24.2
require (
- github.com/charmbracelet/bubbletea v1.3.5
+ github.com/alecthomas/chroma/v2 v2.23.1
+ github.com/charmbracelet/bubbles v1.0.0
+ github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.4.2
)
require (
+ github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
- github.com/charmbracelet/colorprofile
v0.2.3-0.20250311203215-f60798e515dc // indirect
- github.com/charmbracelet/x/ansi v0.8.0 // indirect
- github.com/charmbracelet/x/cellbuf
v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
- github.com/charmbracelet/x/term v0.2.1 // indirect
+ github.com/charmbracelet/colorprofile v0.4.1 // indirect
+ github.com/charmbracelet/x/ansi v0.11.6 // indirect
+ github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
+ github.com/charmbracelet/x/term v0.2.2 // indirect
+ github.com/clipperhouse/displaywidth v0.9.0 // indirect
+ github.com/clipperhouse/stringish v0.1.1 // indirect
+ github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
+ github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f //
indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+ github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
+ github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
- golang.org/x/sync v0.13.0 // indirect
- golang.org/x/sys v0.32.0 // indirect
+ golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.3.8 // indirect
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/go.sum
new/renovate-pretty-log-0.5.2/go.sum
--- old/renovate-pretty-log-0.1.2/go.sum 2025-05-23 18:08:47.000000000
+0200
+++ new/renovate-pretty-log-0.5.2/go.sum 2026-03-06 16:03:10.000000000
+0100
@@ -1,33 +1,53 @@
+github.com/alecthomas/assert/v2 v2.11.0
h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
+github.com/alecthomas/assert/v2 v2.11.0/go.mod
h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
+github.com/alecthomas/chroma/v2 v2.23.1
h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
+github.com/alecthomas/chroma/v2 v2.23.1/go.mod
h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
+github.com/alecthomas/repr v0.5.2
h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
+github.com/alecthomas/repr v0.5.2/go.mod
h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
+github.com/atotto/clipboard v0.1.4
h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
+github.com/atotto/clipboard v0.1.4/go.mod
h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1
h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod
h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
-github.com/charmbracelet/bubbletea v1.3.5
h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
-github.com/charmbracelet/bubbletea v1.3.5/go.mod
h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54=
-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc
h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
-github.com/charmbracelet/colorprofile
v0.2.3-0.20250311203215-f60798e515dc/go.mod
h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
+github.com/charmbracelet/bubbles v1.0.0
h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
+github.com/charmbracelet/bubbles v1.0.0/go.mod
h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
+github.com/charmbracelet/bubbletea v1.3.10
h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
+github.com/charmbracelet/bubbletea v1.3.10/go.mod
h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
+github.com/charmbracelet/colorprofile v0.4.1
h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
+github.com/charmbracelet/colorprofile v0.4.1/go.mod
h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
github.com/charmbracelet/lipgloss v1.1.0
h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod
h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/log v0.4.2
h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
github.com/charmbracelet/log v0.4.2/go.mod
h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
-github.com/charmbracelet/x/ansi v0.8.0
h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
-github.com/charmbracelet/x/ansi v0.8.0/go.mod
h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd
h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
-github.com/charmbracelet/x/cellbuf
v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod
h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
-github.com/charmbracelet/x/term v0.2.1
h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
-github.com/charmbracelet/x/term v0.2.1/go.mod
h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
+github.com/charmbracelet/x/ansi v0.11.6
h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
+github.com/charmbracelet/x/ansi v0.11.6/go.mod
h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
+github.com/charmbracelet/x/cellbuf v0.0.15
h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
+github.com/charmbracelet/x/cellbuf v0.0.15/go.mod
h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
+github.com/charmbracelet/x/term v0.2.2
h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
+github.com/charmbracelet/x/term v0.2.2/go.mod
h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
+github.com/clipperhouse/displaywidth v0.9.0
h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA=
+github.com/clipperhouse/displaywidth v0.9.0/go.mod
h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA=
+github.com/clipperhouse/stringish v0.1.1
h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
+github.com/clipperhouse/stringish v0.1.1/go.mod
h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
+github.com/clipperhouse/uax29/v2 v2.5.0
h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
+github.com/clipperhouse/uax29/v2 v2.5.0/go.mod
h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/davecgh/go-spew v1.1.1
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dlclark/regexp2 v1.11.5
h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
+github.com/dlclark/regexp2 v1.11.5/go.mod
h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f
h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod
h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/go-logfmt/logfmt v0.6.0
h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod
h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-github.com/lucasb-eyer/go-colorful v1.2.0
h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod
h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/hexops/gotextdiff v1.0.3
h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
+github.com/hexops/gotextdiff v1.0.3/go.mod
h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
+github.com/lucasb-eyer/go-colorful v1.3.0
h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
+github.com/lucasb-eyer/go-colorful v1.3.0/go.mod
h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20
h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod
h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1
h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod
h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.16
h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod
h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.19
h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
+github.com/mattn/go-runewidth v0.0.19/go.mod
h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6
h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod
h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2
h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
@@ -36,7 +56,6 @@
github.com/muesli/termenv v0.16.0/go.mod
h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/pmezard/go-difflib v1.0.0
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rivo/uniseg v0.2.0/go.mod
h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod
h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/testify v1.10.0
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
@@ -45,12 +64,10 @@
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod
h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod
h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
-golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
-golang.org/x/sync v0.13.0/go.mod
h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
-golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/internal/style/main.go
new/renovate-pretty-log-0.5.2/internal/style/main.go
--- old/renovate-pretty-log-0.1.2/internal/style/main.go 2025-05-23
18:08:47.000000000 +0200
+++ new/renovate-pretty-log-0.5.2/internal/style/main.go 2026-03-06
16:03:10.000000000 +0100
@@ -4,11 +4,16 @@
"bytes"
"encoding/json"
"fmt"
+ "io"
"regexp"
"slices"
"strings"
"time"
+ "github.com/alecthomas/chroma/v2"
+ "github.com/alecthomas/chroma/v2/formatters"
+ "github.com/alecthomas/chroma/v2/lexers"
+ "github.com/alecthomas/chroma/v2/styles"
"github.com/charmbracelet/lipgloss"
"gitlab.com/tanna.dev/renovate-pretty-log/internal/log"
)
@@ -21,6 +26,59 @@
27, 91, 48, 109, 10, 27, 91, 51, 56, 59, 50, 59, 49, 56, 54, 59, 49,
54, 54, 59, 49, 50, 55, 109, 27, 91, 48, 109, 10,
}
+// Srcery color scheme for Chroma
+var srceryStyle = styles.Register(chroma.MustNewStyle("srcery",
chroma.StyleEntries{
+ chroma.Background: "#1C1B19",
+ chroma.Text: "#BAA67F",
+ chroma.Comment: "#918175",
+ chroma.CommentPreproc: "#68A8E4",
+ chroma.Keyword: "#EF2F27",
+ chroma.KeywordNamespace: "#E02C6D",
+ chroma.KeywordType: "#FBB829",
+ chroma.Operator: "#FF5F00",
+ chroma.Punctuation: "#BAA67F",
+ chroma.Name: "#BAA67F",
+ chroma.NameBuiltin: "#2C78BF",
+ chroma.NameFunction: "#2C78BF",
+ chroma.NameClass: "#FBB829",
+ chroma.NameNamespace: "#E02C6D",
+ chroma.NameException: "#EF2F27",
+ chroma.NameVariable: "#68A8E4",
+ chroma.NameConstant: "#E02C6D",
+ chroma.NameLabel: "#2C78BF",
+ chroma.NameEntity: "#FF8700",
+ chroma.NameAttribute: "#98BC37",
+ chroma.NameTag: "#2C78BF",
+ chroma.NameDecorator: "#E02C6D",
+ chroma.String: "#519F50",
+ chroma.StringDoc: "#918175",
+ chroma.StringInterpol: "#98BC37",
+ chroma.StringEscape: "#0AAEB3",
+ chroma.StringRegex: "#0AAEB3",
+ chroma.StringSymbol: "#519F50",
+ chroma.StringOther: "#519F50",
+ chroma.Number: "#FF8700",
+ chroma.NumberInteger: "#FF8700",
+ chroma.NumberFloat: "#FF8700",
+ chroma.NumberHex: "#FF8700",
+ chroma.NumberOct: "#FF8700",
+ chroma.Literal: "#FF8700",
+ chroma.LiteralDate: "#519F50",
+ chroma.Generic: "#BAA67F",
+ chroma.GenericDeleted: "#EF2F27",
+ chroma.GenericEmph: "italic",
+ chroma.GenericError: "#EF2F27",
+ chroma.GenericHeading: "bold #2C78BF",
+ chroma.GenericInserted: "#519F50",
+ chroma.GenericOutput: "#918175",
+ chroma.GenericPrompt: "#BAA67F",
+ chroma.GenericStrong: "bold",
+ chroma.GenericSubheading: "bold #E02C6D",
+ chroma.GenericTraceback: "#EF2F27",
+ chroma.GenericUnderline: "underline",
+ chroma.Error: "#EF2F27",
+}))
+
var LogLineTraceLevelStyle = lipgloss.NewStyle().
SetString("TRAC").
Bold(true).
@@ -146,6 +204,7 @@
var extras []string
for _, k := range keys {
+ buf.Reset()
v := line.Extras[k]
out := ""
@@ -155,11 +214,21 @@
float64,
string:
out = fmt.Sprintf("%#v", v)
+ err := highlightJSONWithChroma(&buf, out)
+ if err == nil {
+ out = buf.String()
+ }
default:
- buf.Reset()
err := jsonEncoder.Encode(v)
if err == nil {
- out = buf.String()
+ str := buf.String()
+ buf.Reset()
+ err = highlightJSONWithChroma(&buf, str)
+ if err == nil {
+ out = buf.String()
+ } else {
+ out = fmt.Sprintf("%#v", v)
+ }
} else {
out = fmt.Sprintf("%#v", v)
}
@@ -178,3 +247,17 @@
return ""
}
+
+func highlightJSONWithChroma(w io.Writer, jsonText string) error {
+ lexer := lexers.Get("json")
+ if lexer == nil {
+ lexer = lexers.Fallback
+ }
+ iter, err := lexer.Tokenise(nil, jsonText)
+ if err != nil {
+ return err
+ }
+
+ formatter := formatters.Get("terminal256")
+ return formatter.Format(w, srceryStyle, iter)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/renovate-pretty-log-0.1.2/renovate.json
new/renovate-pretty-log-0.5.2/renovate.json
--- old/renovate-pretty-log-0.1.2/renovate.json 1970-01-01 01:00:00.000000000
+0100
+++ new/renovate-pretty-log-0.5.2/renovate.json 2026-03-06 16:03:10.000000000
+0100
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended"
+ ]
+}
++++++ renovate-pretty-log.obsinfo ++++++
--- /var/tmp/diff_new_pack.c6zJW8/_old 2026-03-23 17:14:38.960871431 +0100
+++ /var/tmp/diff_new_pack.c6zJW8/_new 2026-03-23 17:14:38.964871597 +0100
@@ -1,5 +1,5 @@
name: renovate-pretty-log
-version: 0.1.2
-mtime: 1748016527
-commit: c29241031096ff5d3f36a751167f88440a79eae5
+version: 0.5.2
+mtime: 1772809390
+commit: f67ff8d1e2034f80b0b5b2b8be58d15438388c2e
++++++ vendor.tar.gz ++++++
++++ 98004 lines of diff (skipped)