generate: convert to termstatus

This commit is contained in:
Michael Eischer
2025-09-14 10:49:00 +02:00
parent 44dbd4469e
commit 114cc33fe9
2 changed files with 30 additions and 21 deletions

View File

@@ -7,6 +7,8 @@ import (
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/terminal" "github.com/restic/restic/internal/terminal"
"github.com/restic/restic/internal/ui/progress"
"github.com/restic/restic/internal/ui/termstatus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/cobra/doc" "github.com/spf13/cobra/doc"
"github.com/spf13/pflag" "github.com/spf13/pflag"
@@ -30,7 +32,9 @@ Exit status is 1 if there was any error.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
return runGenerate(opts, args) term, cancel := setupTermstatus()
defer cancel()
return runGenerate(opts, globalOptions, args, term)
}, },
} }
opts.AddFlags(cmd.Flags()) opts.AddFlags(cmd.Flags())
@@ -53,7 +57,7 @@ func (opts *generateOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)") f.StringVar(&opts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)")
} }
func writeManpages(root *cobra.Command, dir string) error { func writeManpages(root *cobra.Command, dir string, printer progress.Printer) error {
// use a fixed date for the man pages so that generating them is deterministic // use a fixed date for the man pages so that generating them is deterministic
date, err := time.Parse("Jan 2006", "Jan 2017") date, err := time.Parse("Jan 2006", "Jan 2017")
if err != nil { if err != nil {
@@ -67,13 +71,13 @@ func writeManpages(root *cobra.Command, dir string) error {
Date: &date, Date: &date,
} }
Verbosef("writing man pages to directory %v\n", dir) printer.P("writing man pages to directory %v", dir)
return doc.GenManTree(root, header, dir) return doc.GenManTree(root, header, dir)
} }
func writeCompletion(filename string, shell string, generate func(w io.Writer) error) (err error) { func writeCompletion(filename string, shell string, generate func(w io.Writer) error, printer progress.Printer) (err error) {
if terminal.StdoutIsTerminal() { if terminal.StdoutIsTerminal() {
Verbosef("writing %s completion file to %v\n", shell, filename) printer.P("writing %s completion file to %v", shell, filename)
} }
var outWriter io.Writer var outWriter io.Writer
if filename != "-" { if filename != "-" {
@@ -111,15 +115,16 @@ func checkStdoutForSingleShell(opts generateOptions) error {
return nil return nil
} }
func runGenerate(opts generateOptions, args []string) error { func runGenerate(opts generateOptions, gopts GlobalOptions, args []string, term *termstatus.Terminal) error {
if len(args) > 0 { if len(args) > 0 {
return errors.Fatal("the generate command expects no arguments, only options - please see `restic help generate` for usage and flags") return errors.Fatal("the generate command expects no arguments, only options - please see `restic help generate` for usage and flags")
} }
printer := newTerminalProgressPrinter(gopts.JSON, gopts.verbosity, term)
cmdRoot := newRootCommand() cmdRoot := newRootCommand()
if opts.ManDir != "" { if opts.ManDir != "" {
err := writeManpages(cmdRoot, opts.ManDir) err := writeManpages(cmdRoot, opts.ManDir, printer)
if err != nil { if err != nil {
return err return err
} }
@@ -131,28 +136,28 @@ func runGenerate(opts generateOptions, args []string) error {
} }
if opts.BashCompletionFile != "" { if opts.BashCompletionFile != "" {
err := writeCompletion(opts.BashCompletionFile, "bash", cmdRoot.GenBashCompletion) err := writeCompletion(opts.BashCompletionFile, "bash", cmdRoot.GenBashCompletion, printer)
if err != nil { if err != nil {
return err return err
} }
} }
if opts.FishCompletionFile != "" { if opts.FishCompletionFile != "" {
err := writeCompletion(opts.FishCompletionFile, "fish", func(w io.Writer) error { return cmdRoot.GenFishCompletion(w, true) }) err := writeCompletion(opts.FishCompletionFile, "fish", func(w io.Writer) error { return cmdRoot.GenFishCompletion(w, true) }, printer)
if err != nil { if err != nil {
return err return err
} }
} }
if opts.ZSHCompletionFile != "" { if opts.ZSHCompletionFile != "" {
err := writeCompletion(opts.ZSHCompletionFile, "zsh", cmdRoot.GenZshCompletion) err := writeCompletion(opts.ZSHCompletionFile, "zsh", cmdRoot.GenZshCompletion, printer)
if err != nil { if err != nil {
return err return err
} }
} }
if opts.PowerShellCompletionFile != "" { if opts.PowerShellCompletionFile != "" {
err := writeCompletion(opts.PowerShellCompletionFile, "powershell", cmdRoot.GenPowerShellCompletion) err := writeCompletion(opts.PowerShellCompletionFile, "powershell", cmdRoot.GenPowerShellCompletion, printer)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,13 +1,23 @@
package main package main
import ( import (
"bytes" "context"
"strings" "strings"
"testing" "testing"
rtest "github.com/restic/restic/internal/test" rtest "github.com/restic/restic/internal/test"
"github.com/restic/restic/internal/ui/termstatus"
) )
func testRunGenerate(gopts GlobalOptions, opts generateOptions) ([]byte, error) {
buf, err := withCaptureStdout(gopts, func(gopts GlobalOptions) error {
return withTermStatus(gopts, func(ctx context.Context, term *termstatus.Terminal) error {
return runGenerate(opts, gopts, []string{}, term)
})
})
return buf.Bytes(), err
}
func TestGenerateStdout(t *testing.T) { func TestGenerateStdout(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
@@ -21,20 +31,14 @@ func TestGenerateStdout(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
buf := bytes.NewBuffer(nil) output, err := testRunGenerate(globalOptions, tc.opts)
globalOptions.stdout = buf
err := runGenerate(tc.opts, []string{})
rtest.OK(t, err) rtest.OK(t, err)
completionString := buf.String() rtest.Assert(t, strings.Contains(string(output), "# "+tc.name+" completion for restic"), "has no expected completion header")
rtest.Assert(t, strings.Contains(completionString, "# "+tc.name+" completion for restic"), "has no expected completion header")
}) })
} }
t.Run("Generate shell completions to stdout for two shells", func(t *testing.T) { t.Run("Generate shell completions to stdout for two shells", func(t *testing.T) {
buf := bytes.NewBuffer(nil) _, err := testRunGenerate(globalOptions, generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"})
globalOptions.stdout = buf
opts := generateOptions{BashCompletionFile: "-", FishCompletionFile: "-"}
err := runGenerate(opts, []string{})
rtest.Assert(t, err != nil, "generate shell completions to stdout for two shells fails") rtest.Assert(t, err != nil, "generate shell completions to stdout for two shells fails")
}) })
} }