1
0
Fork 0
mirror of https://github.com/restic/restic.git synced 2024-12-21 07:13:10 +00:00

Merge pull request #5053 from rominf/rominf-generate-stdout

generate: allow passing `-` for stdout output
This commit is contained in:
Michael Eischer 2024-10-09 20:06:54 +00:00 committed by GitHub
commit 7c12bd59a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 93 additions and 28 deletions

View file

@ -0,0 +1,6 @@
Enhancement: Allow generating shell completions to stdout
Restic `generate` now supports passing `-` passed as file name to `--[shell]-completion` option.
https://github.com/restic/restic/issues/2511
https://github.com/restic/restic/pull/5053

View file

@ -1,6 +1,8 @@
package main
import (
"io"
"os"
"time"
"github.com/restic/restic/internal/errors"
@ -41,10 +43,10 @@ func init() {
cmdRoot.AddCommand(cmdGenerate)
fs := cmdGenerate.Flags()
fs.StringVar(&genOpts.ManDir, "man", "", "write man pages to `directory`")
fs.StringVar(&genOpts.BashCompletionFile, "bash-completion", "", "write bash completion `file`")
fs.StringVar(&genOpts.FishCompletionFile, "fish-completion", "", "write fish completion `file`")
fs.StringVar(&genOpts.ZSHCompletionFile, "zsh-completion", "", "write zsh completion `file`")
fs.StringVar(&genOpts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file`")
fs.StringVar(&genOpts.BashCompletionFile, "bash-completion", "", "write bash completion `file` (`-` for stdout)")
fs.StringVar(&genOpts.FishCompletionFile, "fish-completion", "", "write fish completion `file` (`-` for stdout)")
fs.StringVar(&genOpts.ZSHCompletionFile, "zsh-completion", "", "write zsh completion `file` (`-` for stdout)")
fs.StringVar(&genOpts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)")
}
func writeManpages(dir string) error {
@ -65,32 +67,44 @@ func writeManpages(dir string) error {
return doc.GenManTree(cmdRoot, header, dir)
}
func writeBashCompletion(file string) error {
func writeCompletion(filename string, shell string, generate func(w io.Writer) error) (err error) {
if stdoutIsTerminal() {
Verbosef("writing bash completion file to %v\n", file)
Verbosef("writing %s completion file to %v\n", shell, filename)
}
return cmdRoot.GenBashCompletionFile(file)
var outWriter io.Writer
if filename != "-" {
var outFile *os.File
outFile, err = os.Create(filename)
if err != nil {
return
}
defer func() { err = outFile.Close() }()
outWriter = outFile
} else {
outWriter = globalOptions.stdout
}
err = generate(outWriter)
return
}
func writeFishCompletion(file string) error {
if stdoutIsTerminal() {
Verbosef("writing fish completion file to %v\n", file)
func checkStdoutForSingleShell(opts generateOptions) error {
completionFileOpts := []string{
opts.BashCompletionFile,
opts.FishCompletionFile,
opts.ZSHCompletionFile,
opts.PowerShellCompletionFile,
}
return cmdRoot.GenFishCompletionFile(file, true)
}
func writeZSHCompletion(file string) error {
if stdoutIsTerminal() {
Verbosef("writing zsh completion file to %v\n", file)
seenIsStdout := false
for _, completionFileOpt := range completionFileOpts {
if completionFileOpt == "-" {
if seenIsStdout {
return errors.Fatal("the generate command can generate shell completions to stdout for single shell only")
}
seenIsStdout = true
}
}
return cmdRoot.GenZshCompletionFile(file)
}
func writePowerShellCompletion(file string) error {
if stdoutIsTerminal() {
Verbosef("writing powershell completion file to %v\n", file)
}
return cmdRoot.GenPowerShellCompletionFile(file)
return nil
}
func runGenerate(opts generateOptions, args []string) error {
@ -105,29 +119,34 @@ func runGenerate(opts generateOptions, args []string) error {
}
}
err := checkStdoutForSingleShell(opts)
if err != nil {
return err
}
if opts.BashCompletionFile != "" {
err := writeBashCompletion(opts.BashCompletionFile)
err := writeCompletion(opts.BashCompletionFile, "bash", cmdRoot.GenBashCompletion)
if err != nil {
return err
}
}
if opts.FishCompletionFile != "" {
err := writeFishCompletion(opts.FishCompletionFile)
err := writeCompletion(opts.FishCompletionFile, "fish", func(w io.Writer) error { return cmdRoot.GenFishCompletion(w, true) })
if err != nil {
return err
}
}
if opts.ZSHCompletionFile != "" {
err := writeZSHCompletion(opts.ZSHCompletionFile)
err := writeCompletion(opts.ZSHCompletionFile, "zsh", cmdRoot.GenZshCompletion)
if err != nil {
return err
}
}
if opts.PowerShellCompletionFile != "" {
err := writePowerShellCompletion(opts.PowerShellCompletionFile)
err := writeCompletion(opts.PowerShellCompletionFile, "powershell", cmdRoot.GenPowerShellCompletion)
if err != nil {
return err
}

View file

@ -0,0 +1,40 @@
package main
import (
"bytes"
"strings"
"testing"
rtest "github.com/restic/restic/internal/test"
)
func TestGenerateStdout(t *testing.T) {
testCases := []struct {
name string
opts generateOptions
}{
{"bash", generateOptions{BashCompletionFile: "-"}},
{"fish", generateOptions{FishCompletionFile: "-"}},
{"zsh", generateOptions{ZSHCompletionFile: "-"}},
{"powershell", generateOptions{PowerShellCompletionFile: "-"}},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
buf := bytes.NewBuffer(nil)
globalOptions.stdout = buf
err := runGenerate(tc.opts, []string{})
rtest.OK(t, err)
completionString := buf.String()
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) {
buf := bytes.NewBuffer(nil)
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")
})
}