Register hidden stub commands for official GitHub extensions (gh-aw, gh-stack) that offer to install the extension when invoked. This replaces the error-string-matching approach from the original PR with proper cobra commands that: - Avoid false-positive matches on flag values or post-'--' args - Eliminate conflicting cobra 'Did you mean?' suggestions - Properly propagate prompt/install errors for correct exit codes - Are hidden from help output and shell completions - Use GroupID "extension" so checkValidExtension allows installing over them - Are registered after extensions and aliases so both take priority Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
76 lines
2.4 KiB
Go
76 lines
2.4 KiB
Go
package root
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/MakeNowJust/heredoc"
|
|
"github.com/cli/cli/v2/internal/prompter"
|
|
"github.com/cli/cli/v2/pkg/extensions"
|
|
"github.com/cli/cli/v2/pkg/iostreams"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// NewCmdOfficialExtension creates a hidden stub command for an official
|
|
// extension that has not yet been installed. When invoked, it suggests
|
|
// installing the extension and, in interactive sessions, offers to do so
|
|
// immediately. After a successful install, the extension is dispatched with
|
|
// the original arguments.
|
|
func NewCmdOfficialExtension(io *iostreams.IOStreams, p prompter.Prompter, em extensions.ExtensionManager, ext *extensions.OfficialExtension) *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: ext.Name,
|
|
Short: fmt.Sprintf("Install the official %s extension", ext.Name),
|
|
Hidden: true,
|
|
GroupID: "extension",
|
|
Annotations: map[string]string{
|
|
"skipAuthCheck": "true",
|
|
},
|
|
// Accept any args/flags the user may have passed so we don't get
|
|
// cobra validation errors before reaching RunE.
|
|
DisableFlagParsing: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return officialExtensionRun(io, p, em, ext, args)
|
|
},
|
|
}
|
|
}
|
|
|
|
func officialExtensionRun(io *iostreams.IOStreams, p prompter.Prompter, em extensions.ExtensionManager, ext *extensions.OfficialExtension, args []string) error {
|
|
stderr := io.ErrOut
|
|
|
|
if !io.CanPrompt() {
|
|
fmt.Fprint(stderr, heredoc.Docf(`
|
|
%[1]s is available as an official extension.
|
|
To install it, run:
|
|
gh extension install %[2]s/%[3]s
|
|
`, fmt.Sprintf("gh %s", ext.Name), ext.Owner, ext.Repo))
|
|
return nil
|
|
}
|
|
|
|
prompt := heredoc.Docf(`
|
|
%[1]s is available as an official extension.
|
|
Would you like to install it now?
|
|
`, fmt.Sprintf("gh %s", ext.Name))
|
|
confirmed, err := p.Confirm(prompt, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !confirmed {
|
|
return nil
|
|
}
|
|
|
|
repo := ext.Repository()
|
|
io.StartProgressIndicatorWithLabel(fmt.Sprintf("Installing %s/%s...", ext.Owner, ext.Repo))
|
|
installErr := em.Install(repo, "")
|
|
io.StopProgressIndicator()
|
|
if installErr != nil {
|
|
return fmt.Errorf("failed to install extension: %w", installErr)
|
|
}
|
|
|
|
fmt.Fprintf(stderr, "Successfully installed %s/%s\n", ext.Owner, ext.Repo)
|
|
|
|
// Dispatch the newly installed extension with the original arguments.
|
|
dispatchArgs := append([]string{ext.Name}, args...)
|
|
if _, dispatchErr := em.Dispatch(dispatchArgs, io.In, io.Out, stderr); dispatchErr != nil {
|
|
return dispatchErr
|
|
}
|
|
return nil
|
|
}
|