forked from TrueCloudLab/restic
generate: allow passing -
for stdout output
Since generating completions to stdout for multiple shells does not make sense, enforce `-` is supplied only once.
This commit is contained in:
parent
c1532179d4
commit
3d976562fa
3 changed files with 93 additions and 28 deletions
6
changelog/unreleased/issue-2511
Normal file
6
changelog/unreleased/issue-2511
Normal 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
|
|
@ -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")
|
||||
}
|
||||
return cmdRoot.GenZshCompletionFile(file)
|
||||
}
|
||||
|
||||
func writePowerShellCompletion(file string) error {
|
||||
if stdoutIsTerminal() {
|
||||
Verbosef("writing powershell completion file to %v\n", file)
|
||||
seenIsStdout = true
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
40
cmd/restic/cmd_generate_integration_test.go
Normal file
40
cmd/restic/cmd_generate_integration_test.go
Normal 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")
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue