chore: lint and govuln (#775)
build / build (push) Failing after 0s
build / snapshot (push) Failing after 0s
lint / lint (push) Failing after 1s
nightly / nightly (push) Failing after 0s

Signed-off-by: drew <me@andrinoff.com>
This commit is contained in:
Drew Smirnoff
2026-08-11 17:04:25 +04:00
committed by GitHub
parent 0a2c15eab7
commit 2311ac5f36
11 changed files with 62 additions and 52 deletions
+4 -1
View File
@@ -456,6 +456,9 @@ func ExecutePaste(_ parser.Command, v *VHS) error {
return nil
}
// shellSetting is the name of the setting that configures the shell.
const shellSetting = "Shell"
// Settings maps the Set commands to their respective functions.
var Settings = map[string]CommandFunc{
"FontFamily": ExecuteSetFontFamily,
@@ -469,7 +472,7 @@ var Settings = map[string]CommandFunc{
"Theme": ExecuteSetTheme,
"TypingSpeed": ExecuteSetTypingSpeed,
"Width": ExecuteSetWidth,
"Shell": ExecuteSetShell,
shellSetting: ExecuteSetShell,
"LoopOffset": ExecuteLoopOffset,
"MarginFill": ExecuteSetMarginFill,
"Margin": ExecuteSetMargin,
+5 -5
View File
@@ -30,7 +30,7 @@ func Evaluate(ctx context.Context, tape string, out io.Writer, opts ...Evaluator
v := New()
for _, cmd := range cmds {
if cmd.Type == token.SET && cmd.Options == "Shell" || cmd.Type == token.ENV {
if cmd.Type == token.SET && cmd.Options == shellSetting || cmd.Type == token.ENV {
err := Execute(cmd, &v)
if err != nil {
return []error{err}
@@ -39,7 +39,7 @@ func Evaluate(ctx context.Context, tape string, out io.Writer, opts ...Evaluator
}
// Start things up
if err := v.Start(); err != nil {
if err := v.Start(ctx); err != nil {
return []error{err}
}
defer func() { _ = v.close() }()
@@ -56,7 +56,7 @@ func Evaluate(ctx context.Context, tape string, out io.Writer, opts ...Evaluator
for i, cmd := range cmds {
if cmd.Type == token.SET || cmd.Type == token.OUTPUT || cmd.Type == token.REQUIRE {
_, _ = fmt.Fprintln(out, Highlight(cmd, false))
if cmd.Options != "Shell" {
if cmd.Options != shellSetting {
err := Execute(cmd, &v)
if err != nil {
return []error{err}
@@ -111,7 +111,7 @@ func Evaluate(ctx context.Context, tape string, out io.Writer, opts ...Evaluator
}
// Begin recording frames as we are now in a recording state.
ctx, cancel := context.WithCancel(ctx) //nolint:gosec
ctx, cancel := context.WithCancel(ctx)
ch := v.Record(ctx)
// Clean up temporary files at the end.
@@ -182,7 +182,7 @@ func Evaluate(ctx context.Context, tape string, out io.Writer, opts ...Evaluator
}
teardown()
if err := v.Render(); err != nil {
if err := v.Render(ctx); err != nil {
return []error{err}
}
return nil
+8 -8
View File
@@ -1,6 +1,6 @@
module github.com/charmbracelet/vhs
go 1.25.8
go 1.25.12
require (
github.com/agnivade/levenshtein v1.2.1
@@ -21,8 +21,8 @@ require (
github.com/muesli/roff v0.1.0
github.com/muesli/termenv v0.16.0
github.com/spf13/cobra v1.10.2
golang.org/x/crypto v0.49.0
golang.org/x/term v0.41.0
golang.org/x/crypto v0.52.0
golang.org/x/term v0.43.0
)
require (
@@ -64,11 +64,11 @@ require (
github.com/ysmood/got v0.40.0 // indirect
github.com/ysmood/gson v0.7.3 // indirect
github.com/ysmood/leakless v0.9.0 // indirect
github.com/yuin/goldmark v1.7.13 // indirect
github.com/yuin/goldmark v1.7.17 // indirect
github.com/yuin/goldmark-emoji v1.0.6 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/net v0.54.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/text v0.39.0 // indirect
)
+14 -14
View File
@@ -137,27 +137,27 @@ github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE=
github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=
github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark v1.7.17 h1:p36OVWwRb246iHxA/U4p8OPEpOTESm4n+g+8t0EE5uA=
github.com/yuin/goldmark v1.7.17/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs=
github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
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.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+3 -3
View File
@@ -297,8 +297,8 @@ func init() {
var versionRegex = regexp.MustCompile(`\d+\.\d+\.\d+`)
// getVersion returns the parsed version of a program.
func getVersion(program string) *version.Version {
cmd := exec.Command(program, "--version")
func getVersion(ctx context.Context, program string) *version.Version {
cmd := exec.CommandContext(ctx, program, "--version")
out, err := cmd.Output()
if err != nil {
return nil
@@ -323,7 +323,7 @@ func ensureDependencies() error {
return fmt.Errorf("%v is not installed", defaultShell)
}
ttydVersion := getVersion("ttyd")
ttydVersion := getVersion(context.Background(), "ttyd")
if ttydVersion == nil || ttydVersion.LessThan(ttydMinVersion) {
return fmt.Errorf("ttyd version (%s) is out of date, VHS requires %s\n%s",
ttydVersion,
+2 -2
View File
@@ -69,8 +69,8 @@ var EscapeSequences = map[string]string{
// vhs record > file.tape
//
//nolint:wrapcheck
func Record(_ *cobra.Command, _ []string) error {
command := exec.Command(shell)
func Record(cmd *cobra.Command, _ []string) error {
command := exec.CommandContext(cmd.Context(), shell)
command.Env = append(os.Environ(), "VHS_RECORD=true")
+4 -2
View File
@@ -1,6 +1,7 @@
package main
import (
"context"
"fmt"
"os/exec"
"path/filepath"
@@ -50,7 +51,7 @@ func (opts *ScreenshotOptions) enableFrameCapture(path string) {
}
// MakeScreenshots generates screenshots by given ScreenshotOptions.
func MakeScreenshots(opts ScreenshotOptions) []*exec.Cmd {
func MakeScreenshots(ctx context.Context, opts ScreenshotOptions) []*exec.Cmd {
cmds := []*exec.Cmd{} //nolint:prealloc
for path, frame := range opts.screenshots {
@@ -59,7 +60,8 @@ func MakeScreenshots(opts ScreenshotOptions) []*exec.Cmd {
args := opts.buildFFopts(path, textStream, cursorStream)
cmds = append(cmds, exec.Command(
cmds = append(cmds, exec.CommandContext(
ctx,
"ffmpeg",
args...,
))
+2
View File
@@ -1,5 +1,7 @@
// Package token provides the token types and structures for the VHS Tape
// language.
//
//nolint:revive
package token
import (
+3 -2
View File
@@ -10,6 +10,7 @@
package main
import (
"context"
"fmt"
"net"
"os"
@@ -24,7 +25,7 @@ func randomPort() int {
}
// buildTtyCmd builds the ttyd exec.Command on the given port.
func buildTtyCmd(port int, shell Shell) *exec.Cmd {
func buildTtyCmd(ctx context.Context, port int, shell Shell) *exec.Cmd {
args := []string{ //nolint:prealloc
fmt.Sprintf("--port=%d", port),
"--interface", "127.0.0.1",
@@ -38,7 +39,7 @@ func buildTtyCmd(port int, shell Shell) *exec.Cmd {
args = append(args, shell.Command...)
cmd := exec.Command("ttyd", args...)
cmd := exec.CommandContext(ctx, "ttyd", args...)
if shell.Env != nil {
cmd.Env = append(shell.Env, os.Environ()...)
}
+7 -7
View File
@@ -122,7 +122,7 @@ func New() VHS {
}
// Start starts ttyd, browser and everything else needed to create the gif.
func (vhs *VHS) Start() error {
func (vhs *VHS) Start(ctx context.Context) error {
vhs.mutex.Lock()
defer vhs.mutex.Unlock()
@@ -131,7 +131,7 @@ func (vhs *VHS) Start() error {
}
port := randomPort()
vhs.tty = buildTtyCmd(port, vhs.Options.Shell)
vhs.tty = buildTtyCmd(ctx, port, vhs.Options.Shell)
if err := vhs.tty.Start(); err != nil {
return fmt.Errorf("could not start tty: %w", err)
}
@@ -220,7 +220,7 @@ func (vhs *VHS) Cleanup() error {
}
// Render starts rendering the individual frames into a video.
func (vhs *VHS) Render() error {
func (vhs *VHS) Render(ctx context.Context) error {
// Apply Loop Offset by modifying frame sequence
if err := vhs.ApplyLoopOffset(); err != nil {
return err
@@ -228,10 +228,10 @@ func (vhs *VHS) Render() error {
// Generate the video(s) with the frames.
var cmds []*exec.Cmd //nolint:prealloc
cmds = append(cmds, MakeGIF(vhs.Options.Video))
cmds = append(cmds, MakeMP4(vhs.Options.Video))
cmds = append(cmds, MakeWebM(vhs.Options.Video))
cmds = append(cmds, MakeScreenshots(vhs.Options.Screenshot)...)
cmds = append(cmds, MakeGIF(ctx, vhs.Options.Video))
cmds = append(cmds, MakeMP4(ctx, vhs.Options.Video))
cmds = append(cmds, MakeWebM(ctx, vhs.Options.Video))
cmds = append(cmds, MakeScreenshots(ctx, vhs.Options.Screenshot)...)
for _, cmd := range cmds {
if cmd == nil {
+10 -8
View File
@@ -9,6 +9,7 @@
package main
import (
"context"
"fmt"
"log"
"os"
@@ -81,7 +82,7 @@ func marginFillIsColor(marginFill string) bool {
}
// makeMedia takes a list of images (as frames) and converts them to a GIF/WebM/MP4.
func makeMedia(opts VideoOptions, targetFile string) *exec.Cmd {
func makeMedia(ctx context.Context, opts VideoOptions, targetFile string) *exec.Cmd {
if targetFile == "" {
return nil
}
@@ -90,7 +91,8 @@ func makeMedia(opts VideoOptions, targetFile string) *exec.Cmd {
ensureDir(targetFile)
//nolint:gosec
return exec.Command(
return exec.CommandContext(
ctx,
"ffmpeg",
buildFFopts(opts, targetFile)...,
)
@@ -153,16 +155,16 @@ func buildFFopts(opts VideoOptions, targetFile string) []string {
}
// MakeGIF takes a list of images (as frames) and converts them to a GIF.
func MakeGIF(opts VideoOptions) *exec.Cmd {
return makeMedia(opts, opts.Output.GIF)
func MakeGIF(ctx context.Context, opts VideoOptions) *exec.Cmd {
return makeMedia(ctx, opts, opts.Output.GIF)
}
// MakeWebM takes a list of images (as frames) and converts them to a WebM.
func MakeWebM(opts VideoOptions) *exec.Cmd {
return makeMedia(opts, opts.Output.WebM)
func MakeWebM(ctx context.Context, opts VideoOptions) *exec.Cmd {
return makeMedia(ctx, opts, opts.Output.WebM)
}
// MakeMP4 takes a list of images (as frames) and converts them to an MP4.
func MakeMP4(opts VideoOptions) *exec.Cmd {
return makeMedia(opts, opts.Output.MP4)
func MakeMP4(ctx context.Context, opts VideoOptions) *exec.Cmd {
return makeMedia(ctx, opts, opts.Output.MP4)
}