pinning binary exts
This commit is contained in:
parent
e361fd47a3
commit
d7277e396c
5 changed files with 90 additions and 40 deletions
|
|
@ -76,10 +76,12 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
return t.Render()
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "install <repository>",
|
||||
Short: "Install a gh extension from a repository",
|
||||
Long: heredoc.Doc(`
|
||||
func() *cobra.Command {
|
||||
var pinFlag string
|
||||
cmd := &cobra.Command{
|
||||
Use: "install <repository>",
|
||||
Short: "Install a gh extension from a repository",
|
||||
Long: heredoc.Doc(`
|
||||
Install a GitHub repository locally as a GitHub CLI extension.
|
||||
|
||||
The repository argument can be specified in "owner/repo" format as well as a full URL.
|
||||
|
|
@ -90,41 +92,44 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
|
||||
See the list of available extensions at <https://github.com/topics/gh-extension>.
|
||||
`),
|
||||
Example: heredoc.Doc(`
|
||||
Example: heredoc.Doc(`
|
||||
$ gh extension install owner/gh-extension
|
||||
$ gh extension install https://git.example.com/owner/gh-extension
|
||||
$ gh extension install .
|
||||
`),
|
||||
Args: cmdutil.MinimumArgs(1, "must specify a repository to install from"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if args[0] == "." {
|
||||
wd, err := os.Getwd()
|
||||
Args: cmdutil.MinimumArgs(1, "must specify a repository to install from"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if args[0] == "." {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.InstallLocal(wd)
|
||||
}
|
||||
|
||||
repo, err := ghrepo.FromFullName(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.InstallLocal(wd)
|
||||
}
|
||||
|
||||
repo, err := ghrepo.FromFullName(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkValidExtension(cmd.Root(), m, repo.RepoName()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkValidExtension(cmd.Root(), m, repo.RepoName()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.Install(repo, pinFlag); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.Install(repo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if io.IsStdoutTTY() {
|
||||
cs := io.ColorScheme()
|
||||
fmt.Fprintf(io.Out, "%s Installed extension %s\n", cs.SuccessIcon(), args[0])
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
if io.IsStdoutTTY() {
|
||||
cs := io.ColorScheme()
|
||||
fmt.Fprintf(io.Out, "%s Installed extension %s\n", cs.SuccessIcon(), args[0])
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&pinFlag, "pin", "", "pin extension to a release tag or commit sha")
|
||||
return cmd
|
||||
}(),
|
||||
func() *cobra.Command {
|
||||
var flagAll bool
|
||||
var flagForce bool
|
||||
|
|
|
|||
|
|
@ -112,3 +112,37 @@ func fetchLatestRelease(httpClient *http.Client, baseRepo ghrepo.Interface) (*re
|
|||
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// fetchRelease finds release by tag name for a repository
|
||||
func fetchReleaseFromTag(httpClient *http.Client, baseRepo ghrepo.Interface, tagName string) (*release, error) {
|
||||
fullRepoName := fmt.Sprintf("%s/%s", baseRepo.RepoOwner(), baseRepo.RepoName())
|
||||
path := fmt.Sprintf("repos/%s/releases/tags/%s", fullRepoName, tagName)
|
||||
url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode > 299 {
|
||||
return nil, api.HandleHTTPError(resp)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var r release
|
||||
err = json.Unmarshal(b, &r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &r, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,13 +320,13 @@ type binManifest struct {
|
|||
Path string
|
||||
}
|
||||
|
||||
func (m *Manager) Install(repo ghrepo.Interface) error {
|
||||
func (m *Manager) Install(repo ghrepo.Interface, targetCommitish string) error {
|
||||
isBin, err := isBinExtension(m.client, repo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not check for binary extension: %w", err)
|
||||
}
|
||||
if isBin {
|
||||
return m.installBin(repo)
|
||||
return m.installBin(repo, targetCommitish)
|
||||
}
|
||||
|
||||
hs, err := hasScript(m.client, repo)
|
||||
|
|
@ -341,9 +341,14 @@ func (m *Manager) Install(repo ghrepo.Interface) error {
|
|||
return m.installGit(ghrepo.FormatRemoteURL(repo, protocol), m.io.Out, m.io.ErrOut)
|
||||
}
|
||||
|
||||
func (m *Manager) installBin(repo ghrepo.Interface) error {
|
||||
func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) error {
|
||||
var r *release
|
||||
r, err := fetchLatestRelease(m.client, repo)
|
||||
var err error
|
||||
if targetCommitish == "" {
|
||||
r, err = fetchLatestRelease(m.client, repo)
|
||||
} else {
|
||||
r, err = fetchReleaseFromTag(m.client, repo, targetCommitish)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -498,7 +503,7 @@ func (m *Manager) upgradeExtension(ext Extension, force bool) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to migrate to new precompiled extension format: %w", err)
|
||||
}
|
||||
return m.installBin(repo)
|
||||
return m.installBin(repo, "")
|
||||
}
|
||||
err = m.upgradeGitExtension(ext, force)
|
||||
}
|
||||
|
|
@ -525,7 +530,7 @@ func (m *Manager) upgradeBinExtension(ext Extension) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to parse URL %s: %w", ext.url, err)
|
||||
}
|
||||
return m.installBin(repo)
|
||||
return m.installBin(repo, "")
|
||||
}
|
||||
|
||||
func (m *Manager) Remove(name string) error {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ type Extension interface {
|
|||
//go:generate moq -rm -out manager_mock.go . ExtensionManager
|
||||
type ExtensionManager interface {
|
||||
List(includeMetadata bool) []Extension
|
||||
Install(ghrepo.Interface) error
|
||||
Install(ghrepo.Interface, string) error
|
||||
InstallLocal(dir string) error
|
||||
Upgrade(name string, force bool) error
|
||||
Remove(name string) error
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ var _ ExtensionManager = &ExtensionManagerMock{}
|
|||
// DispatchFunc: func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error) {
|
||||
// panic("mock out the Dispatch method")
|
||||
// },
|
||||
// InstallFunc: func(interfaceMoqParam ghrepo.Interface) error {
|
||||
// InstallFunc: func(interfaceMoqParam ghrepo.Interface, s string) error {
|
||||
// panic("mock out the Install method")
|
||||
// },
|
||||
// InstallLocalFunc: func(dir string) error {
|
||||
|
|
@ -54,7 +54,7 @@ type ExtensionManagerMock struct {
|
|||
DispatchFunc func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error)
|
||||
|
||||
// InstallFunc mocks the Install method.
|
||||
InstallFunc func(interfaceMoqParam ghrepo.Interface) error
|
||||
InstallFunc func(interfaceMoqParam ghrepo.Interface, s string) error
|
||||
|
||||
// InstallLocalFunc mocks the InstallLocal method.
|
||||
InstallLocalFunc func(dir string) error
|
||||
|
|
@ -92,6 +92,8 @@ type ExtensionManagerMock struct {
|
|||
Install []struct {
|
||||
// InterfaceMoqParam is the interfaceMoqParam argument value.
|
||||
InterfaceMoqParam ghrepo.Interface
|
||||
// S is the s argument value.
|
||||
S string
|
||||
}
|
||||
// InstallLocal holds details about calls to the InstallLocal method.
|
||||
InstallLocal []struct {
|
||||
|
|
@ -204,19 +206,21 @@ func (mock *ExtensionManagerMock) DispatchCalls() []struct {
|
|||
}
|
||||
|
||||
// Install calls InstallFunc.
|
||||
func (mock *ExtensionManagerMock) Install(interfaceMoqParam ghrepo.Interface) error {
|
||||
func (mock *ExtensionManagerMock) Install(interfaceMoqParam ghrepo.Interface, s string) error {
|
||||
if mock.InstallFunc == nil {
|
||||
panic("ExtensionManagerMock.InstallFunc: method is nil but ExtensionManager.Install was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
InterfaceMoqParam ghrepo.Interface
|
||||
S string
|
||||
}{
|
||||
InterfaceMoqParam: interfaceMoqParam,
|
||||
S: s,
|
||||
}
|
||||
mock.lockInstall.Lock()
|
||||
mock.calls.Install = append(mock.calls.Install, callInfo)
|
||||
mock.lockInstall.Unlock()
|
||||
return mock.InstallFunc(interfaceMoqParam)
|
||||
return mock.InstallFunc(interfaceMoqParam, s)
|
||||
}
|
||||
|
||||
// InstallCalls gets all the calls that were made to Install.
|
||||
|
|
@ -224,9 +228,11 @@ func (mock *ExtensionManagerMock) Install(interfaceMoqParam ghrepo.Interface) er
|
|||
// len(mockedExtensionManager.InstallCalls())
|
||||
func (mock *ExtensionManagerMock) InstallCalls() []struct {
|
||||
InterfaceMoqParam ghrepo.Interface
|
||||
S string
|
||||
} {
|
||||
var calls []struct {
|
||||
InterfaceMoqParam ghrepo.Interface
|
||||
S string
|
||||
}
|
||||
mock.lockInstall.RLock()
|
||||
calls = mock.calls.Install
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue