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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
|
@ -41,10 +43,10 @@ func init() {
|
||||||
cmdRoot.AddCommand(cmdGenerate)
|
cmdRoot.AddCommand(cmdGenerate)
|
||||||
fs := cmdGenerate.Flags()
|
fs := cmdGenerate.Flags()
|
||||||
fs.StringVar(&genOpts.ManDir, "man", "", "write man pages to `directory`")
|
fs.StringVar(&genOpts.ManDir, "man", "", "write man pages to `directory`")
|
||||||
fs.StringVar(&genOpts.BashCompletionFile, "bash-completion", "", "write bash completion `file`")
|
fs.StringVar(&genOpts.BashCompletionFile, "bash-completion", "", "write bash completion `file` (`-` for stdout)")
|
||||||
fs.StringVar(&genOpts.FishCompletionFile, "fish-completion", "", "write fish completion `file`")
|
fs.StringVar(&genOpts.FishCompletionFile, "fish-completion", "", "write fish completion `file` (`-` for stdout)")
|
||||||
fs.StringVar(&genOpts.ZSHCompletionFile, "zsh-completion", "", "write zsh completion `file`")
|
fs.StringVar(&genOpts.ZSHCompletionFile, "zsh-completion", "", "write zsh completion `file` (`-` for stdout)")
|
||||||
fs.StringVar(&genOpts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file`")
|
fs.StringVar(&genOpts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeManpages(dir string) error {
|
func writeManpages(dir string) error {
|
||||||
|
@ -65,32 +67,44 @@ func writeManpages(dir string) error {
|
||||||
return doc.GenManTree(cmdRoot, header, dir)
|
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() {
|
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 {
|
func checkStdoutForSingleShell(opts generateOptions) error {
|
||||||
if stdoutIsTerminal() {
|
completionFileOpts := []string{
|
||||||
Verbosef("writing fish completion file to %v\n", file)
|
opts.BashCompletionFile,
|
||||||
|
opts.FishCompletionFile,
|
||||||
|
opts.ZSHCompletionFile,
|
||||||
|
opts.PowerShellCompletionFile,
|
||||||
}
|
}
|
||||||
return cmdRoot.GenFishCompletionFile(file, true)
|
seenIsStdout := false
|
||||||
}
|
for _, completionFileOpt := range completionFileOpts {
|
||||||
|
if completionFileOpt == "-" {
|
||||||
func writeZSHCompletion(file string) error {
|
if seenIsStdout {
|
||||||
if stdoutIsTerminal() {
|
return errors.Fatal("the generate command can generate shell completions to stdout for single shell only")
|
||||||
Verbosef("writing zsh completion file to %v\n", file)
|
|
||||||
}
|
}
|
||||||
return cmdRoot.GenZshCompletionFile(file)
|
seenIsStdout = true
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
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 != "" {
|
if opts.BashCompletionFile != "" {
|
||||||
err := writeBashCompletion(opts.BashCompletionFile)
|
err := writeCompletion(opts.BashCompletionFile, "bash", cmdRoot.GenBashCompletion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.FishCompletionFile != "" {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ZSHCompletionFile != "" {
|
if opts.ZSHCompletionFile != "" {
|
||||||
err := writeZSHCompletion(opts.ZSHCompletionFile)
|
err := writeCompletion(opts.ZSHCompletionFile, "zsh", cmdRoot.GenZshCompletion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.PowerShellCompletionFile != "" {
|
if opts.PowerShellCompletionFile != "" {
|
||||||
err := writePowerShellCompletion(opts.PowerShellCompletionFile)
|
err := writeCompletion(opts.PowerShellCompletionFile, "powershell", cmdRoot.GenPowerShellCompletion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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