diff --git a/go.mod b/go.mod index 081f6f0..126f6b7 100644 --- a/go.mod +++ b/go.mod @@ -5,31 +5,32 @@ go 1.21 require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/MakeNowJust/heredoc v1.0.0 - github.com/charmbracelet/glamour v0.8.0 - github.com/charmbracelet/lipgloss v0.12.1 + github.com/alecthomas/chroma/v2 v2.14.0 + github.com/charmbracelet/lipgloss v1.0.0 github.com/cli/browser v1.3.0 + github.com/cli/glamour v0.0.0-20250225134531-b1d96ed4a7e1 github.com/cli/safeexec v1.0.0 github.com/cli/shurcooL-graphql v0.0.4 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/henvic/httpretty v0.0.6 github.com/itchyny/gojq v0.12.15 + github.com/leaanthony/go-ansi-parser v1.6.1 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a github.com/stretchr/testify v1.7.0 github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e - golang.org/x/sys v0.28.0 - golang.org/x/term v0.27.0 + golang.org/x/sys v0.30.0 + golang.org/x/term v0.29.0 golang.org/x/text v0.21.0 gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/alecthomas/chroma/v2 v2.14.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/charmbracelet/x/ansi v0.1.4 // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect github.com/gorilla/css v1.0.1 // indirect @@ -40,12 +41,12 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/yuin/goldmark v1.7.4 // indirect - github.com/yuin/goldmark-emoji v1.0.3 // indirect + github.com/yuin/goldmark v1.7.8 // indirect + github.com/yuin/goldmark-emoji v1.0.4 // indirect golang.org/x/net v0.33.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/go.sum b/go.sum index 0e7f62c..76c8f96 100644 --- a/go.sum +++ b/go.sum @@ -16,16 +16,18 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= -github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= -github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs= -github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= -github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= -github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= -github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4 h1:6KzMkQeAF56rggw2NZu1L+TH7j9+DM1/2Kmh7KUxg1I= -github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +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/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= +github.com/cli/glamour v0.0.0-20250220192152-8544502ccff9 h1:fPJNUzG+Au+pIfYx2c5QNngZ3KLj7xAzjutL6efy1x8= +github.com/cli/glamour v0.0.0-20250220192152-8544502ccff9/go.mod h1:bB4uNJ5F0+nzpGqwlhKEy7tD0PPL0SxNWEwZzG77vMg= +github.com/cli/glamour v0.0.0-20250225134531-b1d96ed4a7e1 h1:TrV+Bs2RKR/CeodJDRO6GXzIxT7PAOwbuxth8ACcucg= +github.com/cli/glamour v0.0.0-20250225134531-b1d96ed4a7e1/go.mod h1:bB4uNJ5F0+nzpGqwlhKEy7tD0PPL0SxNWEwZzG77vMg= github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY= @@ -60,8 +62,12 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A= +github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= 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/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -70,8 +76,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ 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-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -97,10 +103,10 @@ github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:Buzhfgf github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= -github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= -github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= -github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= +github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= +github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90= +github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -120,12 +126,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/pkg/markdown/accessibility.go b/pkg/markdown/accessibility.go index 0d1a41e..f618879 100644 --- a/pkg/markdown/accessibility.go +++ b/pkg/markdown/accessibility.go @@ -3,8 +3,8 @@ package markdown import ( "strconv" - "github.com/charmbracelet/glamour/ansi" - "github.com/charmbracelet/glamour/styles" + "github.com/cli/glamour/ansi" + "github.com/cli/glamour/styles" ) type ANSIColorCode int diff --git a/pkg/markdown/accessibility_test.go b/pkg/markdown/accessibility_test.go index 93ac9b1..4b33068 100644 --- a/pkg/markdown/accessibility_test.go +++ b/pkg/markdown/accessibility_test.go @@ -3,8 +3,8 @@ package markdown import ( "testing" - "github.com/charmbracelet/glamour/ansi" - "github.com/charmbracelet/glamour/styles" + "github.com/cli/glamour/ansi" + "github.com/cli/glamour/styles" "github.com/stretchr/testify/assert" ) diff --git a/pkg/markdown/markdown.go b/pkg/markdown/markdown.go index 6960b93..57ef6bf 100644 --- a/pkg/markdown/markdown.go +++ b/pkg/markdown/markdown.go @@ -5,7 +5,7 @@ import ( "os" "strings" - "github.com/charmbracelet/glamour" + "github.com/cli/glamour" "github.com/cli/go-gh/v2/pkg/accessibility" ) @@ -39,7 +39,10 @@ func WithTheme(theme string) glamour.TermRendererOption { switch theme { case "light", "dark": if accessible { - return glamour.WithStyles(AccessibleStyleConfig(theme)) + return glamour.WithOptions( + glamour.WithStyles(AccessibleStyleConfig(theme)), + glamour.WithChromaFormatter("terminal16"), + ) } style = theme default: diff --git a/pkg/markdown/markdown_test.go b/pkg/markdown/markdown_test.go index b9467ea..1b301b2 100644 --- a/pkg/markdown/markdown_test.go +++ b/pkg/markdown/markdown_test.go @@ -7,7 +7,10 @@ import ( "strings" "testing" + "github.com/MakeNowJust/heredoc" + "github.com/alecthomas/chroma/v2/styles" "github.com/cli/go-gh/v2/pkg/accessibility" + ansi "github.com/leaanthony/go-ansi-parser" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -21,12 +24,85 @@ const ( brightMagenta_4bitColorSeq = "\x1b[95;" ) +func Test_Render_Codeblocks(t *testing.T) { + t.Setenv("GLAMOUR_STYLE", "") + + text := heredoc.Docf(` + %[1]s%[1]s%[1]sgo + package main + + import ( + "fmt" + ) + + func main() { + fmt.Println("Hello, world!") + } + %[1]s%[1]s%[1]s + `, "`") + + tests := []struct { + name string + text string + theme string + accessible bool + }{ + { + name: "when the light theme is selected, the codeblock renders using 8-bit colors", + text: text, + theme: "light", + }, + { + name: "when the dark theme is selected, the codeblock renders using 8-bit colors", + text: text, + theme: "dark", + }, + { + name: "when the accessible env var is set and the light theme is selected, the codeblock renders using 4-bit colors", + text: text, + theme: "light", + accessible: true, + }, + { + name: "when the accessible env var is set and the dark theme is selected, the codeblock renders using 4-bit colors", + text: text, + theme: "dark", + accessible: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.accessible { + t.Setenv(accessibility.ACCESSIBILITY_ENV, "true") + } + + out, err := Render(tt.text, WithTheme(tt.theme)) + require.NoError(t, err) + + styledText, err := ansi.Parse(out) + require.NoError(t, err) + + for _, st := range styledText { + if tt.accessible { + require.Equalf(t, st.ColourMode, ansi.Default, "Inaccessible color found in '%s' at %d", st, st.Offset) + require.Falsef(t, st.Faint(), "Inaccessible style found in '%s' at %d", st, st.Offset) + } + } + }) + } +} + // Test_Render verifies that the proper ANSI color codes are applied to the rendered // markdown by examining the ANSI escape sequences in the output for the correct color // match. For more information on ANSI color codes, see // https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit func Test_Render(t *testing.T) { - t.Setenv("GLAMOUR_STYLE", "") + codeBlock := heredoc.Docf(` + %[1]s%[1]s%[1]sgo + fmt.Println("Hello, world!") + %[1]s%[1]s%[1]s + `, "`") + tests := []struct { name string text string @@ -74,14 +150,43 @@ func Test_Render(t *testing.T) { accessibleEnvVar: "true", wantOut: fmt.Sprintf("%s1mh2", brightMagenta_4bitColorSeq), }, + { + name: "when the light theme is selected, the codeblock renders using 8-bit colors", + text: codeBlock, + theme: "light", + wantOut: "\x1b[0m\x1b[38;5;235mfmt\x1b[0m\x1b[38;5;210m.\x1b[0m\x1b[38;5;35mPrintln\x1b[0m\x1b[38;5;210m(\x1b[0m\x1b[38;5;95m\"Hello, world!\"\x1b[0m\x1b[38;5;210m)\x1b[0m", + }, + { + name: "when the dark theme is selected, the codeblock renders using 8-bit colors", + text: codeBlock, + theme: "dark", + wantOut: "\x1b[0m\x1b[38;5;251mfmt\x1b[0m\x1b[38;5;187m.\x1b[0m\x1b[38;5;42mPrintln\x1b[0m\x1b[38;5;187m(\x1b[0m\x1b[38;5;173m\"Hello, world!\"\x1b[0m\x1b[38;5;187m)\x1b[0m", + }, + { + name: "when the accessible env var is set and the light theme is selected, the codeblock renders using 4-bit colors", + text: codeBlock, + theme: "light", + accessibleEnvVar: "true", + wantOut: "\x1b[0m\x1b[30mfmt\x1b[0m\x1b[33m.\x1b[0m\x1b[36mPrintln\x1b[0m\x1b[33m(\x1b[0m\x1b[90m\"Hello, world!\"\x1b[0m\x1b[33m)\x1b[0m", + }, + { + name: "when the accessible env var is set and the dark theme is selected, the codeblock renders using 4-bit colors", + text: codeBlock, + theme: "dark", + accessibleEnvVar: "true", + wantOut: "\x1b[0m\x1b[37mfmt\x1b[0m\x1b[37m.\x1b[0m\x1b[36mPrintln\x1b[0m\x1b[37m(\x1b[0m\x1b[33m\"Hello, world!\"\x1b[0m\x1b[37m)\x1b[0m", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + // Unregister cached chroma style causing codeblock tests to fail based on previous theme + delete(styles.Registry, "charm") t.Setenv(accessibility.ACCESSIBILITY_ENV, tt.accessibleEnvVar) - if tt.styleEnvVar != "" { - tmpDir := t.TempDir() - path := filepath.Join(tmpDir, fmt.Sprintf("%s.json", tt.styleEnvVar)) + if tt.styleEnvVar == "" { + t.Setenv("GLAMOUR_STYLE", "") + } else { + path := filepath.Join(t.TempDir(), fmt.Sprintf("%s.json", tt.styleEnvVar)) err := os.WriteFile(path, []byte(customGlamourStyle(t)), 0644) if err != nil { t.Fatal(err)