Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package goben for openSUSE:Factory checked in at 2026-05-06 19:20:34 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/goben (Old) and /work/SRC/openSUSE:Factory/.goben.new.30200 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "goben" Wed May 6 19:20:34 2026 rev:2 rq:1351206 version:1.0.3 Changes: -------- --- /work/SRC/openSUSE:Factory/goben/goben.changes 2025-10-06 18:07:29.905688058 +0200 +++ /work/SRC/openSUSE:Factory/.goben.new.30200/goben.changes 2026-05-06 19:24:48.398183745 +0200 @@ -1,0 +2,6 @@ +Wed May 6 12:09:58 UTC 2026 - Martin Hauke <[email protected]> + +- Update to version 1.0.3 + * Update dependencies + +------------------------------------------------------------------- Old: ---- goben-1.0.2.tar.gz New: ---- goben-1.0.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ goben.spec ++++++ --- /var/tmp/diff_new_pack.pGXJJZ/_old 2026-05-06 19:24:49.258219224 +0200 +++ /var/tmp/diff_new_pack.pGXJJZ/_new 2026-05-06 19:24:49.262219389 +0200 @@ -1,7 +1,7 @@ # # spec file for package goben # -# Copyright (c) 2021-2025, Martin Hauke <[email protected]> +# Copyright (c) 2021-2026, Martin Hauke <[email protected]> # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: goben -Version: 1.0.2 +Version: 1.0.3 Release: 0 Summary: Measure TCP/UDP transport layer throughput between hosts License: MIT ++++++ goben-1.0.2.tar.gz -> goben-1.0.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goben-1.0.2/build.sh new/goben-1.0.3/build.sh --- old/goben-1.0.2/build.sh 2025-09-16 02:59:15.000000000 +0200 +++ new/goben-1.0.3/build.sh 2026-05-06 06:36:27.000000000 +0200 @@ -1,13 +1,28 @@ #!/bin/bash go install golang.org/x/vuln/cmd/govulncheck@latest -go install github.com/mgechev/revive@latest go install golang.org/x/tools/cmd/deadcode@latest +go install github.com/mgechev/revive@latest +go install honnef.co/go/tools/cmd/staticcheck@latest +go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest +go install github.com/gordonklaus/ineffassign@latest +go install github.com/client9/misspell/cmd/misspell@latest +go install github.com/fzipp/gocyclo/cmd/gocyclo@latest gofmt -s -w . revive ./... +staticcheck ./... + +modernize -fix ./... + +gocyclo -over 15 . + +ineffassign ./... + +misspell . + go mod tidy govulncheck ./... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goben-1.0.2/cmd/goben/main.go new/goben-1.0.3/cmd/goben/main.go --- old/goben-1.0.2/cmd/goben/main.go 2025-09-16 02:59:15.000000000 +0200 +++ new/goben-1.0.3/cmd/goben/main.go 2026-05-06 06:36:27.000000000 +0200 @@ -47,5 +47,7 @@ } log.Printf("client mode, %s protocol", proto) - goben.Open(&app) + if _, err := goben.Open(&app); err != nil { + log.Fatalf("Failed to open connection: %v", err) + } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goben-1.0.2/cmd/goben/main_test.go new/goben-1.0.3/cmd/goben/main_test.go --- old/goben-1.0.2/cmd/goben/main_test.go 2025-09-16 02:59:15.000000000 +0200 +++ new/goben-1.0.3/cmd/goben/main_test.go 2026-05-06 06:36:27.000000000 +0200 @@ -84,7 +84,6 @@ client.TLSCA = "../../test/certs/ca.crt" client.TLSCert = "../../test/certs/client.crt" client.TLSKey = "../../test/certs/client.key" - goben.ValidateAndUpdateConfig(client) assert.NoError(t, goben.ValidateAndUpdateConfig(client)) // a server config diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goben-1.0.2/go.mod new/goben-1.0.3/go.mod --- old/goben-1.0.2/go.mod 2025-09-16 02:59:15.000000000 +0200 +++ new/goben-1.0.3/go.mod 2026-05-06 06:36:27.000000000 +0200 @@ -1,7 +1,7 @@ module github.com/udhos/goben require ( - github.com/guptarohit/asciigraph v0.7.3 + github.com/guptarohit/asciigraph v0.9.0 github.com/stretchr/testify v1.9.0 github.com/wcharczuk/go-chart v2.0.1+incompatible gopkg.in/yaml.v3 v3.0.1 @@ -13,8 +13,8 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/image v0.31.0 // indirect + golang.org/x/image v0.39.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) -go 1.25.1 +go 1.26.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goben-1.0.2/go.sum new/goben-1.0.3/go.sum --- old/goben-1.0.2/go.sum 2025-09-16 02:59:15.000000000 +0200 +++ new/goben-1.0.3/go.sum 2026-05-06 06:36:27.000000000 +0200 @@ -5,8 +5,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/guptarohit/asciigraph v0.7.3 h1:p05XDDn7cBTWiBqWb30mrwxd6oU0claAjqeytllnsPY= -github.com/guptarohit/asciigraph v0.7.3/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= +github.com/guptarohit/asciigraph v0.9.0 h1:MvCSRRVkT2XvU1IO6n92o7l7zqx1DiFaoszOUZQztbY= +github.com/guptarohit/asciigraph v0.9.0/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -20,8 +20,8 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wcharczuk/go-chart v2.0.1+incompatible h1:0pz39ZAycJFF7ju/1mepnk26RLVLBCWz1STcD3doU0A= github.com/wcharczuk/go-chart v2.0.1+incompatible/go.mod h1:PF5tmL4EIx/7Wf+hEkpCqYi5He4u90sw+0+6FhrryuE= -golang.org/x/image v0.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA= -golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA= +golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= +golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goben-1.0.2/goben/config.go new/goben-1.0.3/goben/config.go --- old/goben-1.0.2/goben/config.go 2025-09-16 02:59:15.000000000 +0200 +++ new/goben-1.0.3/goben/config.go 2026-05-06 06:36:27.000000000 +0200 @@ -10,7 +10,7 @@ ) // Version is the current application version. -const Version = "1.0.2" +const Version = "1.0.3" // HostList holds a list of hosts to connect to or listen on. type HostList []string @@ -79,7 +79,7 @@ flagSet := flag.FlagSet{} result.AssignFlags(&flagSet) - flagSet.Parse([]string{}) + _ = flagSet.Parse([]string{}) return &result } @@ -89,7 +89,7 @@ // Set sets HostList value from a comma-separated list. func (h *HostList) Set(value string) error { - for _, hh := range strings.Split(value, ",") { + for hh := range strings.SplitSeq(value, ",") { *h = append(*h, hh) } return nil ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/.goreleaser.yml new/vendor/github.com/guptarohit/asciigraph/.goreleaser.yml --- old/vendor/github.com/guptarohit/asciigraph/.goreleaser.yml 2025-09-16 02:59:15.000000000 +0200 +++ new/vendor/github.com/guptarohit/asciigraph/.goreleaser.yml 2026-05-06 06:36:27.000000000 +0200 @@ -1,3 +1,5 @@ +version: 2 + # Build customization builds: - env: @@ -20,13 +22,16 @@ ignore: - goos: darwin goarch: 386 + - goos: windows + goarch: arm checksum: name_template: '{{ .ProjectName }}_{{ .Version }}_sha512-checksums.txt' algorithm: sha512 # Archive customization archives: - id: tar - format: tar.gz + formats: + - tar.gz files: - LICENSE - README.md @@ -40,7 +45,8 @@ {{- if .Arm }}v{{ .Arm }}{{ end -}} format_overrides: - goos: windows - format: zip + formats: + - zip changelog: sort: asc filters: @@ -51,24 +57,33 @@ github: owner: guptarohit name: asciigraph - # If set to true, will not auto-publish the release. - # Default is false. - draft: true -dockers: - - image_templates: - - 'ghcr.io/guptarohit/asciigraph:{{ .Version }}' - - 'ghcr.io/guptarohit/asciigraph:{{ .Tag }}' - - 'ghcr.io/guptarohit/asciigraph:v{{ .Major }}' - - 'ghcr.io/guptarohit/asciigraph:v{{ .Major }}.{{ .Minor }}' - - 'ghcr.io/guptarohit/asciigraph:latest' + draft: false + mode: keep-existing + replace_existing_artifacts: true +dockers_v2: + - images: + - 'ghcr.io/guptarohit/asciigraph' + tags: + - '{{ .Version }}' + - '{{ .Tag }}' + - 'v{{ .Major }}' + - 'v{{ .Major }}.{{ .Minor }}' + - 'latest' dockerfile: goreleaser.dockerfile - build_flag_templates: - - '--label=org.opencontainers.image.title={{ .ProjectName }}' - - '--label=org.opencontainers.image.name={{ .ProjectName }}' - - '--label=org.opencontainers.image.description=Go package to make lightweight line graphs ╭┈╯ in CLI' - - '--label=org.opencontainers.image.url=https://github.com/guptarohit/asciigraph' - - '--label=org.opencontainers.image.source=https://github.com/guptarohit/asciigraph' - - '--label=org.opencontainers.image.version={{ .Version }}' - - '--label=org.opencontainers.image.created={{ .Date }}' - - '--label=org.opencontainers.image.revision={{ .FullCommit }}' - - '--label=org.opencontainers.image.licenses=BSD-3-Clause' + platforms: + - linux/amd64 + - linux/arm64 + build_args: + VERSION: '{{ .Version }}' + COMMIT: '{{ .FullCommit }}' + DATE: '{{ .Date }}' + labels: + org.opencontainers.image.title: '{{ .ProjectName }}' + org.opencontainers.image.name: '{{ .ProjectName }}' + org.opencontainers.image.description: 'Go package to make lightweight line graphs ╭┈╯ in CLI' + org.opencontainers.image.url: 'https://github.com/guptarohit/asciigraph' + org.opencontainers.image.source: 'https://github.com/guptarohit/asciigraph' + org.opencontainers.image.version: '{{ .Version }}' + org.opencontainers.image.created: '{{ .Date }}' + org.opencontainers.image.revision: '{{ .FullCommit }}' + org.opencontainers.image.licenses: 'BSD-3-Clause' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/.release-please-manifest.json new/vendor/github.com/guptarohit/asciigraph/.release-please-manifest.json --- old/vendor/github.com/guptarohit/asciigraph/.release-please-manifest.json 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/guptarohit/asciigraph/.release-please-manifest.json 2026-05-06 06:36:27.000000000 +0200 @@ -0,0 +1,3 @@ +{ + ".": "0.9.0" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/CHANGELOG.md new/vendor/github.com/guptarohit/asciigraph/CHANGELOG.md --- old/vendor/github.com/guptarohit/asciigraph/CHANGELOG.md 2025-09-16 02:59:15.000000000 +0200 +++ new/vendor/github.com/guptarohit/asciigraph/CHANGELOG.md 2026-05-06 06:36:27.000000000 +0200 @@ -2,6 +2,53 @@ All notable changes to this project will be documented in this file. +## [0.9.0](https://github.com/guptarohit/asciigraph/compare/v0.8.1...v0.9.0) (2026-03-28) + + +### Added + +* **plot:** add X-axis rendering with tick marks and labels ([#78](https://github.com/guptarohit/asciigraph/pull/78)) +* **cli:** add -xmin, -xmax, and -xt flags for X-axis support ([#78](https://github.com/guptarohit/asciigraph/pull/78)) + + +### Fixed + +* **docker:** add OCI labels to Dockerfile for ghcr.io metadata ([#76](https://github.com/guptarohit/asciigraph/issues/76)) ([02f3e4a](https://github.com/guptarohit/asciigraph/commit/02f3e4a0f5136d710be39c407f77cfffc384821b)) + +## [0.8.1](https://github.com/guptarohit/asciigraph/compare/v0.8.0...v0.8.1) (2026-03-08) + + +### Fixed + +* 32-bit compile overflow and release workflow cleanup ([451bece](https://github.com/guptarohit/asciigraph/commit/451becef290c1c8e1d114d73cbe60a05fb5e7c0c)) +* 32-bit compile overflow and release workflow cleanup ([#74](https://github.com/guptarohit/asciigraph/issues/74)) ([7f76123](https://github.com/guptarohit/asciigraph/commit/7f7612366a79f8ef909c872dcb5df677cf4a9fbb)) + + +### Changed + +* **axis:** remove minNumLength sentinel from formatter path ([8a81a67](https://github.com/guptarohit/asciigraph/commit/8a81a6718d59db385822e454ecc2e5a387011845)) + +## [0.8.0] - (2026-03-08) + + +### Added + +* Option to format Y-axis values with `YAxisValueFormatter(...)` ([#58](https://github.com/guptarohit/asciigraph/pull/58)) +* Option to customize plot characters with `SeriesChars(...)` and `CreateCharSet(...)` ([#70](https://github.com/guptarohit/asciigraph/pull/70)) +* CLI: `-x` flag to specify custom characters, including comma-separated values for multiple series ([#70](https://github.com/guptarohit/asciigraph/pull/70)) +* Option to configure line endings for raw terminals ([#71](https://github.com/guptarohit/asciigraph/pull/71)) + + +### Fixed + +* Respect caller-specified precision for large numbers ([#69](https://github.com/guptarohit/asciigraph/pull/69)) +* Preserve exact Y-axis values for series with identical data points ([#65](https://github.com/guptarohit/asciigraph/pull/65)), closes [#61](https://github.com/guptarohit/asciigraph/issues/61) +* Prevent panics when legends are set without series colors ([#64](https://github.com/guptarohit/asciigraph/pull/64)) + +### Changed + +* Automates the release flow, update goreleaser config for v2 ([#72](https://github.com/guptarohit/asciigraph/pull/72)) + ## [0.7.3] - 2024-10-26 ### Fixed @@ -99,6 +146,8 @@ - Prevent panics when data is flat. (#8) - Prevent BADPREC issue when maximum and minimum values in a series are 0. (#10) +[0.8.0]: https://github.com/guptarohit/asciigraph/releases/tag/v0.8.0 +[0.7.3]: https://github.com/guptarohit/asciigraph/releases/tag/v0.7.3 [0.7.2]: https://github.com/guptarohit/asciigraph/releases/tag/v0.7.2 [0.7.1]: https://github.com/guptarohit/asciigraph/releases/tag/v0.7.1 [0.7.0]: https://github.com/guptarohit/asciigraph/releases/tag/v0.7.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/CONTRIBUTING.md new/vendor/github.com/guptarohit/asciigraph/CONTRIBUTING.md --- old/vendor/github.com/guptarohit/asciigraph/CONTRIBUTING.md 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/guptarohit/asciigraph/CONTRIBUTING.md 2026-05-06 06:36:27.000000000 +0200 @@ -0,0 +1,69 @@ +# Contributing + +Thanks for contributing to `asciigraph`. + +## Before opening a PR + +- format Go code with `gofmt` +- run `go test ./...` +- keep changes focused and backward compatible where possible + +## Pull request titles + +This repository uses Conventional Commit style PR titles because release +automation relies on them. + +Examples: + +- `feat(axis): add YAxisValueFormatter` +- `fix(plot): preserve values for flat series` +- `docs: update README examples` +- `refactor: simplify legend padding` +- `test(plot): add regression coverage` + +Recommended types: + +- `feat` +- `fix` +- `docs` +- `test` +- `refactor` +- `perf` +- `build` +- `ci` +- `chore` +- `deps` +- `revert` + +If a PR title is not in the expected format, maintainers may edit it before +merge. + +## Commit messages + +Conventional Commit style commit messages are welcome, but they are not +required. + +Release automation relies on the pull request title, so contributors do not need +to rewrite individual commits just to match the release format. + +Versioning notes: + +- `feat` -> minor release +- `fix` -> patch release +- `!` or `BREAKING CHANGE` -> major release + +## Merge guidance for maintainers + +If merge commits are used, keep the final merge commit title aligned with the PR +title so release automation can infer the release correctly from git history. + +## Release process + +Releases are automated: + +- `release-please` opens a release PR and updates `CHANGELOG.md` +- merging the release PR creates the tag and GitHub release +- GoReleaser publishes binaries, checksums, and container images + +Please do not manually edit `CHANGELOG.md` for normal releases unless a +maintainer is intentionally correcting release notes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/README.md new/vendor/github.com/guptarohit/asciigraph/README.md --- old/vendor/github.com/guptarohit/asciigraph/README.md 2025-09-16 02:59:15.000000000 +0200 +++ new/vendor/github.com/guptarohit/asciigraph/README.md 2026-05-06 06:36:27.000000000 +0200 @@ -73,6 +73,88 @@ 0.00 ┼╯ ╰ ``` +### Custom Y-axis value formatting + +Use `YAxisValueFormatter(...)` to control how values printed on the Y-axis are rendered. +This is useful for human-readable units like bytes, durations, or domain-specific labels. + +```go +package main + +import ( + "fmt" + + "github.com/guptarohit/asciigraph" +) + +func main() { + data := []float64{ + 30 * 1024 * 1024 * 1024, + 70 * 1024 * 1024 * 1024, + 2 * 1024 * 1024 * 1024, + } + + graph := asciigraph.Plot(data, + asciigraph.Height(5), + asciigraph.Width(45), + asciigraph.YAxisValueFormatter(func(v float64) string { + return fmt.Sprintf("%.2f GiB", v/1024/1024/1024) + }), + ) + + fmt.Println(graph) +} +``` + +Running this example would render the following graph: +```bash + 70.00 GiB ┤ ╭──────╮ + 56.40 GiB ┤ ╭───────╯ ╰────╮ + 42.80 GiB ┤ ╭──────╯ ╰───╮ + 29.20 GiB ┼──╯ ╰────╮ + 15.60 GiB ┤ ╰───╮ + 2.00 GiB ┤ ╰─ +``` + +### X-axis Support + +Use `XAxisRange(min, max)` to add a labeled X-axis below the graph. +`XAxisTickCount(n)` controls how many tick marks appear (default 5, minimum 2). + +```go +package main + +import ( + "fmt" + "github.com/guptarohit/asciigraph" +) + +func main() { + data := []float64{3, 4, 9, 6, 2, 4, 5, 8, 5, 10, 2, 7, 2, 5, 6} + graph := asciigraph.Plot(data, + asciigraph.XAxisRange(0, 14), + asciigraph.XAxisTickCount(3), + ) + + fmt.Println(graph) +} +``` + +Running this example would render the following graph: +```bash + 10.00 ┤ ╭╮ + 9.00 ┤ ╭╮ ││ + 8.00 ┤ ││ ╭╮││ + 7.00 ┤ ││ ││││╭╮ + 6.00 ┤ │╰╮ ││││││ ╭ + 5.00 ┤ │ │ ╭╯╰╯│││╭╯ + 4.00 ┤╭╯ │╭╯ ││││ + 3.00 ┼╯ ││ ││││ + 2.00 ┤ ╰╯ ╰╯╰╯ + └┬──────┬──────┬ + 0 7 14 +``` + ### Colored graphs ```go @@ -205,18 +287,24 @@ upper bound set the maximum value for the vertical axis (ignored if series contains larger values) (default -Inf) -w width width in columns, 0 for auto-scaling + -xmax value + x-axis maximum value (default NaN) + -xmin value + x-axis minimum value (default NaN) + -xt tick count + x-axis tick count (default 5, minimum 2) asciigraph expects data points from stdin. Invalid values are logged to stderr. ``` Feed it data points via stdin: ```bash -seq 1 72 | asciigraph -h 10 -c "plot data from stdin" +seq 1 72 | asciigraph -h 10 -c "plot data from stdin" -xmin 0 -xmax 40 -xt 5 ``` or use Docker image: ```bash -seq 1 72 | docker run -i --rm ghcr.io/guptarohit/asciigraph -h 10 -c "plot data from stdin" +seq 1 72 | docker run -i --rm ghcr.io/guptarohit/asciigraph -h 10 -c "plot data from stdin" -xmin 0 -xmax 40 -xt 5 ``` Output: @@ -233,6 +321,8 @@ 15.20 ┤ ╭──────╯ 8.10 ┤ ╭──────╯ 1.00 ┼──╯ + └┬─────────────────┬─────────────────┬────────────────┬─────────────────┬ + 0 10 20 30 40 plot data from stdin ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/asciigraph.go new/vendor/github.com/guptarohit/asciigraph/asciigraph.go --- old/vendor/github.com/guptarohit/asciigraph/asciigraph.go 2025-09-16 02:59:15.000000000 +0200 +++ new/vendor/github.com/guptarohit/asciigraph/asciigraph.go 2026-05-06 06:36:27.000000000 +0200 @@ -5,6 +5,7 @@ "fmt" "math" "strings" + "unicode/utf8" ) // Plot returns ascii graph for a series. @@ -12,12 +13,53 @@ return PlotMany([][]float64{series}, options...) } +// getCharSet returns the CharSet for a given series index, falling back to DefaultCharSet. +func getCharSet(config *config, seriesIndex int) CharSet { + if seriesIndex < len(config.SeriesChars) { + charSet := config.SeriesChars[seriesIndex] + // Fill in any empty fields with defaults + if charSet.Horizontal == "" { + charSet.Horizontal = DefaultCharSet.Horizontal + } + if charSet.VerticalLine == "" { + charSet.VerticalLine = DefaultCharSet.VerticalLine + } + if charSet.ArcDownRight == "" { + charSet.ArcDownRight = DefaultCharSet.ArcDownRight + } + if charSet.ArcDownLeft == "" { + charSet.ArcDownLeft = DefaultCharSet.ArcDownLeft + } + if charSet.ArcUpRight == "" { + charSet.ArcUpRight = DefaultCharSet.ArcUpRight + } + if charSet.ArcUpLeft == "" { + charSet.ArcUpLeft = DefaultCharSet.ArcUpLeft + } + if charSet.EndCap == "" { + charSet.EndCap = DefaultCharSet.EndCap + } + if charSet.StartCap == "" { + charSet.StartCap = DefaultCharSet.StartCap + } + if charSet.UpRight == "" { + charSet.UpRight = DefaultCharSet.UpRight + } + if charSet.DownHorizontal == "" { + charSet.DownHorizontal = DefaultCharSet.DownHorizontal + } + return charSet + } + return DefaultCharSet +} + // PlotMany returns ascii graph for multiple series. func PlotMany(data [][]float64, options ...Option) string { var logMaximum float64 config := configure(config{ - Offset: 3, - Precision: 2, + Offset: 3, + Precision: nil, + LineEnding: "\n", }, options) // Create a deep copy of the input data @@ -103,7 +145,11 @@ plot[i] = line } - precision := config.Precision + var precision uint = 2 //Default precision to maintain backwards compatibility + if config.Precision != nil { + precision = *config.Precision + } + logMaximum = math.Log10(math.Max(math.Abs(maximum), math.Abs(minimum))) //to find number of zeros after decimal if minimum == float64(0) && maximum == float64(0) { logMaximum = float64(-1) @@ -117,26 +163,55 @@ } else { precision += uint(math.Abs(logMaximum) - 1.0) } - } else if logMaximum > 2 { + } else if logMaximum > 2 && config.Precision == nil { precision = 0 } - maxNumLength := len(fmt.Sprintf("%0.*f", precision, maximum)) - minNumLength := len(fmt.Sprintf("%0.*f", precision, minimum)) - maxWidth := int(math.Max(float64(maxNumLength), float64(minNumLength))) + maxNumLength := utf8.RuneCountInString(fmt.Sprintf("%0.*f", precision, maximum)) + minNumLength := utf8.RuneCountInString(fmt.Sprintf("%0.*f", precision, minimum)) + magnitudes := make([]float64, 0, rows+1) + if config.YAxisValueFormatter != nil { + maxNumLength = 0 + } - // axis and labels + // calculate Y-axis values and the width when formatted using the YAxisValueFormatter for y := intmin2; y < intmax2+1; y++ { var magnitude float64 - if rows > 0 { + if rows > 0 && interval > 0 { magnitude = maximum - (float64(y-intmin2) * interval / float64(rows)) + } else if interval == 0 { + magnitude = minimum } else { magnitude = float64(y) } + magnitudes = append(magnitudes, magnitude) - label := fmt.Sprintf("%*.*f", maxWidth+1, precision, magnitude) - w := y - intmin2 - h := int(math.Max(float64(config.Offset)-float64(len(label)), 0)) + if config.YAxisValueFormatter != nil { + l := utf8.RuneCountInString(config.YAxisValueFormatter(magnitude)) + if l > maxNumLength { + maxNumLength = l + } + } + } + var maxWidth int + if config.YAxisValueFormatter != nil { + maxWidth = maxNumLength + } else { + maxWidth = int(math.Max(float64(maxNumLength), float64(minNumLength))) + } + leftPad := config.Offset + maxWidth + + // axis and labels reusing the previously calculated values + for w, magnitude := range magnitudes { + var label string + if config.YAxisValueFormatter == nil { + label = fmt.Sprintf("%*.*f", maxWidth+1, precision, magnitude) + } else { + val := config.YAxisValueFormatter(magnitude) + label = strings.Repeat(" ", maxWidth+1-utf8.RuneCountInString(val)) + val + } + + h := int(math.Max(float64(config.Offset)-float64(utf8.RuneCountInString(label)), 0)) plot[w][h].Text = label plot[w][h].Color = config.LabelColor @@ -152,6 +227,9 @@ color = config.SeriesColors[i] } + // Get the character set for this series + charSet := getCharSet(config, i) + var y0, y1 int if !math.IsNaN(series[0]) { @@ -170,14 +248,14 @@ if math.IsNaN(d1) && !math.IsNaN(d0) { y0 = int(round(d0*ratio) - float64(intmin2)) - plot[rows-y0][x+config.Offset].Text = "╴" + plot[rows-y0][x+config.Offset].Text = charSet.EndCap plot[rows-y0][x+config.Offset].Color = color continue } if math.IsNaN(d0) && !math.IsNaN(d1) { y1 = int(round(d1*ratio) - float64(intmin2)) - plot[rows-y1][x+config.Offset].Text = "╶" + plot[rows-y1][x+config.Offset].Text = charSet.StartCap plot[rows-y1][x+config.Offset].Color = color continue } @@ -186,20 +264,20 @@ y1 = int(round(d1*ratio) - float64(intmin2)) if y0 == y1 { - plot[rows-y0][x+config.Offset].Text = "─" + plot[rows-y0][x+config.Offset].Text = charSet.Horizontal } else { if y0 > y1 { - plot[rows-y1][x+config.Offset].Text = "╰" - plot[rows-y0][x+config.Offset].Text = "╮" + plot[rows-y1][x+config.Offset].Text = charSet.ArcUpRight + plot[rows-y0][x+config.Offset].Text = charSet.ArcDownLeft } else { - plot[rows-y1][x+config.Offset].Text = "╭" - plot[rows-y0][x+config.Offset].Text = "╯" + plot[rows-y1][x+config.Offset].Text = charSet.ArcDownRight + plot[rows-y0][x+config.Offset].Text = charSet.ArcUpLeft } start := int(math.Min(float64(y0), float64(y1))) + 1 end := int(math.Max(float64(y0), float64(y1))) for y := start; y < end; y++ { - plot[rows-y][x+config.Offset].Text = "│" + plot[rows-y][x+config.Offset].Text = charSet.VerticalLine } } @@ -215,7 +293,7 @@ var lines bytes.Buffer for h, horizontal := range plot { if h != 0 { - lines.WriteRune('\n') + lines.WriteString(config.LineEnding) } // remove trailing spaces @@ -241,10 +319,15 @@ } } + // add x-axis if configured + if config.XAxisRange != nil { + addXAxis(&lines, config, lenMax, leftPad) + } + // add caption if not empty if config.Caption != "" { - lines.WriteRune('\n') - lines.WriteString(strings.Repeat(" ", config.Offset+maxWidth)) + lines.WriteString(config.LineEnding) + lines.WriteString(strings.Repeat(" ", leftPad)) if len(config.Caption) < lenMax { lines.WriteString(strings.Repeat(" ", (lenMax-len(config.Caption))/2)) } @@ -258,8 +341,161 @@ } if len(config.SeriesLegends) > 0 { - addLegends(&lines, config, lenMax, config.Offset+maxWidth) + addLegends(&lines, config, lenMax, leftPad) } return lines.String() } + +// defaultXAxisFormatter formats X-axis tick values using %g. +var defaultXAxisFormatter XAxisValueFormatterFunc = func(v float64) string { + return fmt.Sprintf("%g", v) +} + +// addXAxis appends an X-axis line and tick labels below the plot body. +func addXAxis(lines *bytes.Buffer, config *config, lenMax int, leftPad int) { + if lenMax <= 0 { + return + } + + xMin := config.XAxisRange[0] + xMax := config.XAxisRange[1] + + tickCount := config.XAxisTickCount + if lenMax == 1 { + tickCount = 1 + } else if tickCount < 2 { + tickCount = 5 + } + if tickCount > lenMax { + tickCount = lenMax + } + + formatter := config.XAxisValueFormatter + + // compute tick column positions and values + type tick struct { + col int + value float64 + label string + } + ticks := make([]tick, tickCount) + for i := 0; i < tickCount; i++ { + if tickCount == 1 { + ticks[i].col = 0 + ticks[i].value = xMin + } else { + ticks[i].value = xMin + float64(i)/float64(tickCount-1)*(xMax-xMin) + ticks[i].col = int(math.Round(float64(lenMax-1) * float64(i) / float64(tickCount-1))) + } + } + + // select formatter: when using default, auto-detect precision based on visible ticks + if formatter == nil { + // simulate overlap with %g labels to find visible ticks with fractional values + hasDecimal := false + lastEnd := -1 + for i := range ticks { + label := defaultXAxisFormatter(ticks[i].value) + labelLen := utf8.RuneCountInString(label) + startCol := leftPad + ticks[i].col - labelLen/2 + if startCol < 0 { + startCol = 0 + } + if startCol > lastEnd { + if ticks[i].value != math.Floor(ticks[i].value) { + hasDecimal = true + break + } + lastEnd = startCol + labelLen + } + } + if hasDecimal { + formatter = func(v float64) string { return fmt.Sprintf("%.2f", v) } + } else { + formatter = defaultXAxisFormatter + } + } + + // format labels + for i := range ticks { + ticks[i].label = formatter(ticks[i].value) + } + + // axis line: leftPad-1 spaces + └ + ─/┬ characters + totalWidth := leftPad + lenMax + axisLine := make([]rune, totalWidth) + for i := range axisLine { + axisLine[i] = ' ' + } + axisLine[leftPad-1] = []rune(DefaultCharSet.UpRight)[0] + for i := 0; i < lenMax; i++ { + axisLine[leftPad+i] = []rune(DefaultCharSet.Horizontal)[0] + } + for _, tk := range ticks { + axisLine[leftPad+tk.col] = []rune(DefaultCharSet.DownHorizontal)[0] + } + + // write axis line with colors + lines.WriteString(config.LineEnding) + axisStr := strings.TrimRight(string(axisLine), " ") + if config.AxisColor != Default { + lines.WriteString(config.AxisColor.String()) + } + lines.WriteString(axisStr) + if config.AxisColor != Default { + lines.WriteString(Default.String()) + } + + // label line: place each label centered on its tick column + maxRightExtent := totalWidth + for _, tk := range ticks { + labelLen := utf8.RuneCountInString(tk.label) + endCol := leftPad + tk.col + (labelLen - labelLen/2) + if endCol > maxRightExtent { + maxRightExtent = endCol + } + } + labelLine := make([]rune, maxRightExtent) + for i := range labelLine { + labelLine[i] = ' ' + } + + lastEnd := -1 // tracks the rightmost column used by the previous label + for _, tk := range ticks { + labelRunes := []rune(tk.label) + labelLen := len(labelRunes) + + // center the label on the tick column + startCol := leftPad + tk.col - labelLen/2 + if startCol < 0 { + startCol = 0 + } + + // skip if this label would overlap the previous one (need 1-space gap) + if startCol <= lastEnd { + continue + } + + for j, r := range labelRunes { + pos := startCol + j + if pos < len(labelLine) { + labelLine[pos] = r + } + } + lastEnd = startCol + labelLen + } + + // trim and write label line + labelStr := strings.TrimRight(string(labelLine), " ") + if labelStr != "" { + lines.WriteString(config.LineEnding) + if config.LabelColor != Default { + lines.WriteString(config.LabelColor.String()) + } + lines.WriteString(labelStr) + if config.LabelColor != Default { + lines.WriteString(Default.String()) + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/goreleaser.dockerfile new/vendor/github.com/guptarohit/asciigraph/goreleaser.dockerfile --- old/vendor/github.com/guptarohit/asciigraph/goreleaser.dockerfile 2025-09-16 02:59:15.000000000 +0200 +++ new/vendor/github.com/guptarohit/asciigraph/goreleaser.dockerfile 2026-05-06 06:36:27.000000000 +0200 @@ -1,3 +1,16 @@ FROM scratch -COPY asciigraph /asciigraph +ARG TARGETPLATFORM +ARG VERSION +ARG COMMIT +ARG DATE +COPY $TARGETPLATFORM/asciigraph /asciigraph +LABEL org.opencontainers.image.title="asciigraph" \ + org.opencontainers.image.name="asciigraph" \ + org.opencontainers.image.description="Go package to make lightweight line graphs ╭┈╯ in CLI" \ + org.opencontainers.image.url="https://github.com/guptarohit/asciigraph" \ + org.opencontainers.image.source="https://github.com/guptarohit/asciigraph" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.created="${DATE}" \ + org.opencontainers.image.revision="${COMMIT}" \ + org.opencontainers.image.licenses="BSD-3-Clause" ENTRYPOINT ["/asciigraph"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/legend.go new/vendor/github.com/guptarohit/asciigraph/legend.go --- old/vendor/github.com/guptarohit/asciigraph/legend.go 2025-09-16 02:59:15.000000000 +0200 +++ new/vendor/github.com/guptarohit/asciigraph/legend.go 2026-05-06 06:36:27.000000000 +0200 @@ -21,14 +21,21 @@ // Add legend for each series added to the graph func addLegends(lines *bytes.Buffer, config *config, lenMax int, leftPad int) { - lines.WriteString("\n\n") + lines.WriteString(config.LineEnding) + lines.WriteString(config.LineEnding) lines.WriteString(strings.Repeat(" ", leftPad)) var legendsText string var legendsTextLen int rightPad := 3 for i, text := range config.SeriesLegends { - item, itemLen := createLegendItem(text, config.SeriesColors[i]) + // Use default color if SeriesColors is not set or index is out of range + color := Default + if i < len(config.SeriesColors) { + color = config.SeriesColors[i] + } + + item, itemLen := createLegendItem(text, color) legendsText += item legendsTextLen += itemLen diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/options.go new/vendor/github.com/guptarohit/asciigraph/options.go --- old/vendor/github.com/guptarohit/asciigraph/options.go 2025-09-16 02:59:15.000000000 +0200 +++ new/vendor/github.com/guptarohit/asciigraph/options.go 2026-05-06 06:36:27.000000000 +0200 @@ -4,6 +4,51 @@ "strings" ) +// CharSet defines the characters used for plotting a series. +type CharSet struct { + Horizontal string // Horizontal line character (default: ─) + VerticalLine string // Vertical line character (default: │) + ArcDownRight string // Arc character going down and right (default: ╭) + ArcDownLeft string // Arc character going down and left (default: ╮) + ArcUpRight string // Arc character going up and right (default: ╰) + ArcUpLeft string // Arc character going up and left (default: ╯) + EndCap string // End cap character (default: ╴) + StartCap string // Start cap character (default: ╶) + UpRight string // Axis corner character (default: └) + DownHorizontal string // X-axis tick mark character (default: ┬) +} + +// DefaultCharSet provides the default box-drawing characters. +var DefaultCharSet = CharSet{ + Horizontal: "─", + VerticalLine: "│", + ArcDownRight: "╭", + ArcDownLeft: "╮", + ArcUpRight: "╰", + ArcUpLeft: "╯", + EndCap: "╴", + StartCap: "╶", + UpRight: "└", + DownHorizontal: "┬", +} + +// CreateCharSet is a helper function that creates a CharSet with all fields set to the same character. +// This is useful for simple uniform character sets like "*", "•", "#", etc. +func CreateCharSet(char string) CharSet { + return CharSet{ + Horizontal: char, + VerticalLine: char, + ArcDownRight: char, + ArcDownLeft: char, + ArcUpRight: char, + ArcUpLeft: char, + EndCap: char, + StartCap: char, + UpRight: char, + DownHorizontal: char, + } +} + // Option represents a configuration setting. type Option interface { apply(c *config) @@ -15,14 +60,26 @@ LowerBound, UpperBound *float64 Offset int Caption string - Precision uint + Precision *uint CaptionColor AnsiColor AxisColor AnsiColor LabelColor AnsiColor SeriesColors []AnsiColor SeriesLegends []string + LineEnding string + SeriesChars []CharSet + YAxisValueFormatter YAxisValueFormatterFunc + XAxisRange *[2]float64 + XAxisTickCount int + XAxisValueFormatter XAxisValueFormatterFunc } +// YAxisValueFormatterFunc formats a single Y-axis value. +type YAxisValueFormatterFunc func(float64) string + +// XAxisValueFormatterFunc formats a single X-axis tick value. +type XAxisValueFormatterFunc func(float64) string + // An optionFunc applies an option. type optionFunc func(*config) @@ -33,6 +90,9 @@ for _, o := range options { o.apply(&defaults) } + if defaults.LineEnding == "" { + defaults.LineEnding = "\n" + } return &defaults } @@ -80,7 +140,7 @@ // Precision sets the graphs precision. func Precision(p uint) Option { - return optionFunc(func(c *config) { c.Precision = p }) + return optionFunc(func(c *config) { c.Precision = &p }) } // Caption sets the graphs caption. @@ -124,3 +184,50 @@ c.SeriesLegends = text }) } + +// LineEnding sets the line ending sequence. Use "\r\n" for raw terminals +// (e.g., Windows terminals) or "\n" for standard Unix-style output. +// Defaults to "\n". +func LineEnding(ending string) Option { + return optionFunc(func(c *config) { + c.LineEnding = ending + }) +} + +// SeriesChars sets the character sets for each series. +// If fewer CharSets are provided than series, DefaultCharSet is used for remaining series. +func SeriesChars(charSets ...CharSet) Option { + return optionFunc(func(c *config) { + c.SeriesChars = charSets + }) +} + +// YAxisValueFormatter formats values printed on the Y-axis. +func YAxisValueFormatter(f YAxisValueFormatterFunc) Option { + return optionFunc(func(c *config) { + c.YAxisValueFormatter = f + }) +} + +// XAxisRange enables the X-axis and maps the given domain [min, max] onto the plot width. +func XAxisRange(min, max float64) Option { + return optionFunc(func(c *config) { + c.XAxisRange = &[2]float64{min, max} + }) +} + +// XAxisTickCount sets the number of ticks on the X-axis. Default is 5, minimum is 2. +func XAxisTickCount(n int) Option { + return optionFunc(func(c *config) { + if n >= 2 { + c.XAxisTickCount = n + } + }) +} + +// XAxisValueFormatter formats values printed on the X-axis. +func XAxisValueFormatter(f XAxisValueFormatterFunc) Option { + return optionFunc(func(c *config) { + c.XAxisValueFormatter = f + }) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/guptarohit/asciigraph/release-please-config.json new/vendor/github.com/guptarohit/asciigraph/release-please-config.json --- old/vendor/github.com/guptarohit/asciigraph/release-please-config.json 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/guptarohit/asciigraph/release-please-config.json 2026-05-06 06:36:27.000000000 +0200 @@ -0,0 +1,59 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "go", + "include-v-in-tag": true, + "pull-request-title-pattern": "chore: release ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Added" + }, + { + "type": "fix", + "section": "Fixed" + }, + { + "type": "perf", + "section": "Changed" + }, + { + "type": "refactor", + "section": "Changed" + }, + { + "type": "build", + "section": "Changed" + }, + { + "type": "deps", + "section": "Changed" + }, + { + "type": "docs", + "section": "Documentation", + "hidden": true + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "ci", + "section": "CI", + "hidden": true + }, + { + "type": "chore", + "section": "Chores", + "hidden": true + } + ], + "packages": { + ".": { + "release-type": "go", + "changelog-path": "CHANGELOG.md", + "release-as": "0.9.0" + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2025-09-16 02:59:15.000000000 +0200 +++ new/vendor/modules.txt 2026-05-06 06:36:27.000000000 +0200 @@ -7,7 +7,7 @@ ## explicit github.com/golang/freetype/raster github.com/golang/freetype/truetype -# github.com/guptarohit/asciigraph v0.7.3 +# github.com/guptarohit/asciigraph v0.9.0 ## explicit; go 1.11 github.com/guptarohit/asciigraph # github.com/kr/pretty v0.3.1 @@ -26,8 +26,8 @@ github.com/wcharczuk/go-chart/roboto github.com/wcharczuk/go-chart/seq github.com/wcharczuk/go-chart/util -# golang.org/x/image v0.31.0 -## explicit; go 1.24.0 +# golang.org/x/image v0.39.0 +## explicit; go 1.25.0 golang.org/x/image/draw golang.org/x/image/font golang.org/x/image/math/f64
