Merge pull request #4308 from cli/ext-bin

install binary extensions
This commit is contained in:
Nate Smith 2021-09-23 10:51:37 -07:00 committed by GitHub
commit 96aed38819
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 512 additions and 49 deletions

View file

@ -101,16 +101,12 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
if err != nil {
return err
}
if err := checkValidExtension(cmd.Root(), m, repo.RepoName()); err != nil {
return err
}
cfg, err := f.Config()
if err != nil {
return err
}
protocol, _ := cfg.Get(repo.RepoHost(), "git_protocol")
return m.Install(ghrepo.FormatRemoteURL(repo, protocol), io.Out, io.ErrOut)
return m.Install(repo)
},
},
func() *cobra.Command {

View file

@ -3,14 +3,17 @@ package extension
import (
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"testing"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/extensions"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
@ -39,13 +42,13 @@ func TestNewCmdExtension(t *testing.T) {
em.ListFunc = func(bool) []extensions.Extension {
return []extensions.Extension{}
}
em.InstallFunc = func(s string, out, errOut io.Writer) error {
em.InstallFunc = func(_ ghrepo.Interface) error {
return nil
}
return func(t *testing.T) {
installCalls := em.InstallCalls()
assert.Equal(t, 1, len(installCalls))
assert.Equal(t, "https://github.com/owner/gh-some-ext.git", installCalls[0].URL)
assert.Equal(t, "gh-some-ext", installCalls[0].InterfaceMoqParam.RepoName())
listCalls := em.ListCalls()
assert.Equal(t, 1, len(listCalls))
}
@ -281,12 +284,19 @@ func TestNewCmdExtension(t *testing.T) {
assertFunc = tt.managerStubs(em)
}
reg := httpmock.Registry{}
defer reg.Verify(t)
client := http.Client{Transport: &reg}
f := cmdutil.Factory{
Config: func() (config.Config, error) {
return config.NewBlankConfig(), nil
},
IOStreams: ios,
ExtensionManager: em,
HttpClient: func() (*http.Client, error) {
return &client, nil
},
}
cmd := NewCmdExtension(&f)

114
pkg/cmd/extension/http.go Normal file
View file

@ -0,0 +1,114 @@
package extension
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghinstance"
"github.com/cli/cli/v2/internal/ghrepo"
)
func hasScript(httpClient *http.Client, repo ghrepo.Interface) (hs bool, err error) {
path := fmt.Sprintf("repos/%s/%s/contents/%s",
repo.RepoOwner(), repo.RepoName(), repo.RepoName())
url := ghinstance.RESTPrefix(repo.RepoHost()) + path
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return
}
resp, err := httpClient.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode == 404 {
return
}
if resp.StatusCode > 299 {
err = api.HandleHTTPError(resp)
return
}
hs = true
return
}
type releaseAsset struct {
Name string
APIURL string `json:"url"`
}
type release struct {
Tag string `json:"tag_name"`
Assets []releaseAsset
}
// downloadAsset downloads a single asset to the given file path.
func downloadAsset(httpClient *http.Client, asset releaseAsset, destPath string) error {
req, err := http.NewRequest("GET", asset.APIURL, nil)
if err != nil {
return err
}
req.Header.Set("Accept", "application/octet-stream")
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode > 299 {
return api.HandleHTTPError(resp)
}
f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0755)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
return err
}
// fetchLatestRelease finds the latest published release for a repository.
func fetchLatestRelease(httpClient *http.Client, baseRepo ghrepo.Interface) (*release, error) {
path := fmt.Sprintf("repos/%s/%s/releases/latest", baseRepo.RepoOwner(), baseRepo.RepoName())
url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
resp, err := httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
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
}

View file

@ -6,6 +6,7 @@ import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path"
@ -14,10 +15,14 @@ import (
"strings"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/extensions"
"github.com/cli/cli/v2/pkg/findsh"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/cli/safeexec"
"gopkg.in/yaml.v3"
)
type Manager struct {
@ -25,17 +30,32 @@ type Manager struct {
lookPath func(string) (string, error)
findSh func() (string, error)
newCommand func(string, ...string) *exec.Cmd
platform func() string
client *http.Client
config config.Config
io *iostreams.IOStreams
}
func NewManager() *Manager {
func NewManager(io *iostreams.IOStreams) *Manager {
return &Manager{
dataDir: config.DataDir,
lookPath: safeexec.LookPath,
findSh: findsh.Find,
newCommand: exec.Command,
platform: func() string {
return fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH)
},
}
}
func (m *Manager) SetConfig(cfg config.Config) {
m.config = cfg
}
func (m *Manager) SetClient(client *http.Client) {
m.client = client
}
func (m *Manager) Dispatch(args []string, stdin io.Reader, stdout, stderr io.Writer) (bool, error) {
if len(args) == 0 {
return false, errors.New("too few arguments in list")
@ -172,7 +192,104 @@ func (m *Manager) InstallLocal(dir string) error {
return makeSymlink(dir, targetLink)
}
func (m *Manager) Install(cloneURL string, stdout, stderr io.Writer) error {
type binManifest struct {
Owner string
Name string
Host string
Tag string
// TODO I may end up not using this; just thinking ahead to local installs
Path string
}
func (m *Manager) Install(repo ghrepo.Interface) 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)
}
hs, err := hasScript(m.client, repo)
if err != nil {
return err
}
if !hs {
// TODO open an issue hint, here?
return errors.New("extension is uninstallable: missing executable")
}
protocol, _ := m.config.Get(repo.RepoHost(), "git_protocol")
return m.installGit(ghrepo.FormatRemoteURL(repo, protocol), m.io.Out, m.io.ErrOut)
}
func (m *Manager) installBin(repo ghrepo.Interface) error {
var r *release
r, err := fetchLatestRelease(m.client, repo)
if err != nil {
return err
}
suffix := m.platform()
var asset *releaseAsset
for _, a := range r.Assets {
if strings.HasSuffix(a.Name, suffix) {
asset = &a
break
}
}
if asset == nil {
return fmt.Errorf("%s unsupported for %s. Open an issue: `gh issue create -R %s/%s -t'Support %s'`",
repo.RepoName(),
suffix, repo.RepoOwner(), repo.RepoName(), suffix)
}
name := repo.RepoName()
targetDir := filepath.Join(m.installDir(), name)
// TODO clean this up if function errs?
err = os.MkdirAll(targetDir, 0755)
if err != nil {
return fmt.Errorf("failed to create installation directory: %w", err)
}
binPath := filepath.Join(targetDir, name)
err = downloadAsset(m.client, *asset, binPath)
if err != nil {
return fmt.Errorf("failed to download asset %s: %w", asset.Name, err)
}
manifest := binManifest{
Name: name,
Owner: repo.RepoOwner(),
Host: repo.RepoHost(),
Path: binPath,
Tag: r.Tag,
}
bs, err := yaml.Marshal(manifest)
if err != nil {
return fmt.Errorf("failed to serialize manifest: %w", err)
}
manifestPath := filepath.Join(targetDir, "manifest.yml")
f, err := os.OpenFile(manifestPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return fmt.Errorf("failed to open manifest for writing: %w", err)
}
defer f.Close()
_, err = f.Write(bs)
if err != nil {
return fmt.Errorf("failed write manifest file: %w", err)
}
return nil
}
func (m *Manager) installGit(cloneURL string, stdout, stderr io.Writer) error {
exe, err := m.lookPath("git")
if err != nil {
return err
@ -346,3 +463,77 @@ func readPathFromFile(path string) (string, error) {
n, err := f.Read(b)
return strings.TrimSpace(string(b[:n])), err
}
func isBinExtension(client *http.Client, repo ghrepo.Interface) (isBin bool, err error) {
var r *release
r, err = fetchLatestRelease(client, repo)
if err != nil {
httpErr, ok := err.(api.HTTPError)
if ok && httpErr.StatusCode == 404 {
err = nil
return
}
return
}
for _, a := range r.Assets {
dists := possibleDists()
for _, d := range dists {
if strings.HasSuffix(a.Name, d) {
isBin = true
break
}
}
}
return
}
func possibleDists() []string {
return []string{
"aix-ppc64",
"android-386",
"android-amd64",
"android-arm",
"android-arm64",
"darwin-amd64",
"darwin-arm64",
"dragonfly-amd64",
"freebsd-386",
"freebsd-amd64",
"freebsd-arm",
"freebsd-arm64",
"illumos-amd64",
"ios-amd64",
"ios-arm64",
"js-wasm",
"linux-386",
"linux-amd64",
"linux-arm",
"linux-arm64",
"linux-mips",
"linux-mips64",
"linux-mips64le",
"linux-mipsle",
"linux-ppc64",
"linux-ppc64le",
"linux-riscv64",
"linux-s390x",
"netbsd-386",
"netbsd-amd64",
"netbsd-arm",
"netbsd-arm64",
"openbsd-386",
"openbsd-amd64",
"openbsd-arm",
"openbsd-arm64",
"openbsd-mips64",
"plan9-386",
"plan9-amd64",
"plan9-arm",
"solaris-amd64",
"windows-386",
"windows-amd64",
"windows-arm",
}
}

View file

@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
@ -11,7 +12,12 @@ import (
"testing"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
func TestHelperProcess(t *testing.T) {
@ -28,7 +34,7 @@ func TestHelperProcess(t *testing.T) {
os.Exit(0)
}
func newTestManager(dir string) *Manager {
func newTestManager(dir string, client *http.Client, io *iostreams.IOStreams) *Manager {
return &Manager{
dataDir: func() string { return dir },
lookPath: func(exe string) (string, error) { return exe, nil },
@ -39,6 +45,12 @@ func newTestManager(dir string) *Manager {
cmd.Env = []string{"GH_WANT_HELPER_PROCESS=1"}
return cmd
},
config: config.NewBlankConfig(),
io: io,
client: client,
platform: func() string {
return "windows-amd64"
},
}
}
@ -47,7 +59,7 @@ func TestManager_List(t *testing.T) {
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-hello", "gh-hello")))
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-two", "gh-two")))
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
exts := m.List(false)
assert.Equal(t, 2, len(exts))
assert.Equal(t, "hello", exts[0].Name())
@ -59,7 +71,7 @@ func TestManager_Dispatch(t *testing.T) {
extPath := filepath.Join(tempDir, "extensions", "gh-hello", "gh-hello")
assert.NoError(t, stubExtension(extPath))
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
@ -80,7 +92,7 @@ func TestManager_Remove(t *testing.T) {
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-hello", "gh-hello")))
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-two", "gh-two")))
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
err := m.Remove("hello")
assert.NoError(t, err)
@ -96,7 +108,7 @@ func TestManager_Upgrade_AllExtensions(t *testing.T) {
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-two", "gh-two")))
assert.NoError(t, stubLocalExtension(tempDir, filepath.Join(tempDir, "extensions", "gh-local", "gh-local")))
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
@ -121,7 +133,7 @@ func TestManager_Upgrade_RemoteExtension(t *testing.T) {
tempDir := t.TempDir()
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-remote", "gh-remote")))
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
@ -141,7 +153,7 @@ func TestManager_Upgrade_LocalExtension(t *testing.T) {
tempDir := t.TempDir()
assert.NoError(t, stubLocalExtension(tempDir, filepath.Join(tempDir, "extensions", "gh-local", "gh-local")))
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
@ -158,7 +170,7 @@ func TestManager_Upgrade_Force(t *testing.T) {
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-remote", "gh-remote")))
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
@ -180,7 +192,7 @@ func TestManager_Upgrade_Force(t *testing.T) {
func TestManager_Upgrade_NoExtensions(t *testing.T) {
tempDir := t.TempDir()
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
@ -190,24 +202,153 @@ func TestManager_Upgrade_NoExtensions(t *testing.T) {
assert.Equal(t, "", stderr.String())
}
func TestManager_Install(t *testing.T) {
func TestManager_Install_git(t *testing.T) {
tempDir := t.TempDir()
m := newTestManager(tempDir)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
err := m.Install("https://github.com/owner/gh-some-ext.git", stdout, stderr)
reg := httpmock.Registry{}
defer reg.Verify(t)
client := http.Client{Transport: &reg}
io, _, stdout, stderr := iostreams.Test()
m := newTestManager(tempDir, &client, io)
reg.Register(
httpmock.REST("GET", "repos/owner/gh-some-ext/releases/latest"),
httpmock.JSONResponse(
release{
Assets: []releaseAsset{
{
Name: "not-a-binary",
APIURL: "https://example.com/release/cool",
},
},
}))
reg.Register(
httpmock.REST("GET", "repos/owner/gh-some-ext/contents/gh-some-ext"),
httpmock.StringResponse("script"))
repo := ghrepo.New("owner", "gh-some-ext")
err := m.Install(repo)
assert.NoError(t, err)
assert.Equal(t, fmt.Sprintf("[git clone https://github.com/owner/gh-some-ext.git %s]\n", filepath.Join(tempDir, "extensions", "gh-some-ext")), stdout.String())
assert.Equal(t, "", stderr.String())
}
func TestManager_Install_binary_unsupported(t *testing.T) {
repo := ghrepo.NewWithHost("owner", "gh-bin-ext", "example.com")
reg := httpmock.Registry{}
defer reg.Verify(t)
client := http.Client{Transport: &reg}
reg.Register(
httpmock.REST("GET", "api/v3/repos/owner/gh-bin-ext/releases/latest"),
httpmock.JSONResponse(
release{
Assets: []releaseAsset{
{
Name: "gh-bin-ext-linux-amd64",
APIURL: "https://example.com/release/cool",
},
},
}))
reg.Register(
httpmock.REST("GET", "api/v3/repos/owner/gh-bin-ext/releases/latest"),
httpmock.JSONResponse(
release{
Tag: "v1.0.1",
Assets: []releaseAsset{
{
Name: "gh-bin-ext-linux-amd64",
APIURL: "https://example.com/release/cool",
},
},
}))
io, _, _, _ := iostreams.Test()
tempDir := t.TempDir()
m := newTestManager(tempDir, &client, io)
err := m.Install(repo)
assert.Error(t, err)
errText := "gh-bin-ext unsupported for windows-amd64. Open an issue: `gh issue create -R owner/gh-bin-ext -t'Support windows-amd64'`"
assert.Equal(t, errText, err.Error())
}
func TestManager_Install_binary(t *testing.T) {
repo := ghrepo.NewWithHost("owner", "gh-bin-ext", "example.com")
reg := httpmock.Registry{}
defer reg.Verify(t)
client := http.Client{Transport: &reg}
reg.Register(
httpmock.REST("GET", "api/v3/repos/owner/gh-bin-ext/releases/latest"),
httpmock.JSONResponse(
release{
Assets: []releaseAsset{
{
Name: "gh-bin-ext-windows-amd64",
APIURL: "https://example.com/release/cool",
},
},
}))
reg.Register(
httpmock.REST("GET", "api/v3/repos/owner/gh-bin-ext/releases/latest"),
httpmock.JSONResponse(
release{
Tag: "v1.0.1",
Assets: []releaseAsset{
{
Name: "gh-bin-ext-windows-amd64",
APIURL: "https://example.com/release/cool",
},
},
}))
reg.Register(
httpmock.REST("GET", "release/cool"),
httpmock.StringResponse("FAKE BINARY"))
io, _, _, _ := iostreams.Test()
tempDir := t.TempDir()
m := newTestManager(tempDir, &client, io)
err := m.Install(repo)
assert.NoError(t, err)
manifest, err := os.ReadFile(filepath.Join(tempDir, "extensions/gh-bin-ext/manifest.yml"))
assert.NoError(t, err)
var bm binManifest
err = yaml.Unmarshal(manifest, &bm)
assert.NoError(t, err)
assert.Equal(t, binManifest{
Name: "gh-bin-ext",
Owner: "owner",
Host: "example.com",
Tag: "v1.0.1",
Path: filepath.Join(tempDir, "extensions/gh-bin-ext/gh-bin-ext"),
}, bm)
fakeBin, err := os.ReadFile(filepath.Join(tempDir, "extensions/gh-bin-ext/gh-bin-ext"))
assert.NoError(t, err)
assert.Equal(t, "FAKE BINARY", string(fakeBin))
}
func TestManager_Create(t *testing.T) {
tempDir := t.TempDir()
oldWd, _ := os.Getwd()
assert.NoError(t, os.Chdir(tempDir))
t.Cleanup(func() { _ = os.Chdir(oldWd) })
m := newTestManager(tempDir)
m := newTestManager(tempDir, nil, nil)
err := m.Create("gh-test")
assert.NoError(t, err)
files, err := ioutil.ReadDir(filepath.Join(tempDir, "gh-test"))

View file

@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"os"
"time"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/context"
@ -22,7 +23,6 @@ func New(appVersion string) *cmdutil.Factory {
Branch: branchFunc(), // No factory dependencies
Executable: executable(), // No factory dependencies
ExtensionManager: extension.NewManager(),
}
f.IOStreams = ioStreams(f) // Depends on Config
@ -30,6 +30,7 @@ func New(appVersion string) *cmdutil.Factory {
f.Remotes = remotesFunc(f) // Depends on Config
f.BaseRepo = BaseRepoFunc(f) // Depends on Remotes
f.Browser = browser(f) // Depends on Config, and IOStreams
f.ExtensionManager = extensionManager(f) // Depends on Config, HttpClient, and IOStreams
return f
}
@ -148,6 +149,25 @@ func branchFunc() func() (string, error) {
}
}
func extensionManager(f *cmdutil.Factory) *extension.Manager {
em := extension.NewManager(f.IOStreams)
cfg, err := f.Config()
if err != nil {
return em
}
em.SetConfig(cfg)
client, err := f.HttpClient()
if err != nil {
return em
}
em.SetClient(api.NewCachedClient(client, time.Second*30))
return em
}
func ioStreams(f *cmdutil.Factory) *iostreams.IOStreams {
io := iostreams.System()
cfg, err := f.Config()

View file

@ -2,6 +2,8 @@ package extensions
import (
"io"
"github.com/cli/cli/v2/internal/ghrepo"
)
//go:generate moq -rm -out extension_mock.go . Extension
@ -16,7 +18,7 @@ type Extension interface {
//go:generate moq -rm -out manager_mock.go . ExtensionManager
type ExtensionManager interface {
List(includeMetadata bool) []Extension
Install(url string, stdout, stderr io.Writer) error
Install(ghrepo.Interface) error
InstallLocal(dir string) error
Upgrade(name string, force bool, stdout, stderr io.Writer) error
Remove(name string) error

View file

@ -4,6 +4,7 @@
package extensions
import (
"github.com/cli/cli/v2/internal/ghrepo"
"io"
"sync"
)
@ -24,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(url string, stdout io.Writer, stderr io.Writer) error {
// InstallFunc: func(interfaceMoqParam ghrepo.Interface) error {
// panic("mock out the Install method")
// },
// InstallLocalFunc: func(dir string) error {
@ -53,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(url string, stdout io.Writer, stderr io.Writer) error
InstallFunc func(interfaceMoqParam ghrepo.Interface) error
// InstallLocalFunc mocks the InstallLocal method.
InstallLocalFunc func(dir string) error
@ -87,12 +88,8 @@ type ExtensionManagerMock struct {
}
// Install holds details about calls to the Install method.
Install []struct {
// URL is the url argument value.
URL string
// Stdout is the stdout argument value.
Stdout io.Writer
// Stderr is the stderr argument value.
Stderr io.Writer
// InterfaceMoqParam is the interfaceMoqParam argument value.
InterfaceMoqParam ghrepo.Interface
}
// InstallLocal holds details about calls to the InstallLocal method.
InstallLocal []struct {
@ -205,37 +202,29 @@ func (mock *ExtensionManagerMock) DispatchCalls() []struct {
}
// Install calls InstallFunc.
func (mock *ExtensionManagerMock) Install(url string, stdout io.Writer, stderr io.Writer) error {
func (mock *ExtensionManagerMock) Install(interfaceMoqParam ghrepo.Interface) error {
if mock.InstallFunc == nil {
panic("ExtensionManagerMock.InstallFunc: method is nil but ExtensionManager.Install was just called")
}
callInfo := struct {
URL string
Stdout io.Writer
Stderr io.Writer
InterfaceMoqParam ghrepo.Interface
}{
URL: url,
Stdout: stdout,
Stderr: stderr,
InterfaceMoqParam: interfaceMoqParam,
}
mock.lockInstall.Lock()
mock.calls.Install = append(mock.calls.Install, callInfo)
mock.lockInstall.Unlock()
return mock.InstallFunc(url, stdout, stderr)
return mock.InstallFunc(interfaceMoqParam)
}
// InstallCalls gets all the calls that were made to Install.
// Check the length with:
// len(mockedExtensionManager.InstallCalls())
func (mock *ExtensionManagerMock) InstallCalls() []struct {
URL string
Stdout io.Writer
Stderr io.Writer
InterfaceMoqParam ghrepo.Interface
} {
var calls []struct {
URL string
Stdout io.Writer
Stderr io.Writer
InterfaceMoqParam ghrepo.Interface
}
mock.lockInstall.RLock()
calls = mock.calls.Install