Merge pull request #4373 from cli/ext-bin-upgrade
binary extensions list & upgrade
This commit is contained in:
commit
af812e2bdc
8 changed files with 337 additions and 97 deletions
|
|
@ -132,7 +132,7 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
if len(args) > 0 {
|
||||
name = normalizeExtensionSelector(args[0])
|
||||
}
|
||||
return m.Upgrade(name, flagForce, io.Out, io.ErrOut)
|
||||
return m.Upgrade(name, flagForce)
|
||||
},
|
||||
}
|
||||
cmd.Flags().BoolVar(&flagAll, "all", false, "Upgrade all extensions")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package extension
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
|
@ -94,7 +93,7 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
name: "upgrade an extension",
|
||||
args: []string{"upgrade", "hello"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.UpgradeFunc = func(name string, force bool, out, errOut io.Writer) error {
|
||||
em.UpgradeFunc = func(name string, force bool) error {
|
||||
return nil
|
||||
}
|
||||
return func(t *testing.T) {
|
||||
|
|
@ -108,7 +107,7 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
name: "upgrade an extension gh-prefix",
|
||||
args: []string{"upgrade", "gh-hello"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.UpgradeFunc = func(name string, force bool, out, errOut io.Writer) error {
|
||||
em.UpgradeFunc = func(name string, force bool) error {
|
||||
return nil
|
||||
}
|
||||
return func(t *testing.T) {
|
||||
|
|
@ -122,7 +121,7 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
name: "upgrade an extension full name",
|
||||
args: []string{"upgrade", "monalisa/gh-hello"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.UpgradeFunc = func(name string, force bool, out, errOut io.Writer) error {
|
||||
em.UpgradeFunc = func(name string, force bool) error {
|
||||
return nil
|
||||
}
|
||||
return func(t *testing.T) {
|
||||
|
|
@ -136,7 +135,7 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
name: "upgrade all",
|
||||
args: []string{"upgrade", "--all"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.UpgradeFunc = func(name string, force bool, out, errOut io.Writer) error {
|
||||
em.UpgradeFunc = func(name string, force bool) error {
|
||||
return nil
|
||||
}
|
||||
return func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const manifestName = "manifest.yml"
|
||||
|
||||
type Extension struct {
|
||||
path string
|
||||
url string
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func downloadAsset(httpClient *http.Client, asset releaseAsset, destPath string)
|
|||
return api.HandleHTTPError(resp)
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0755)
|
||||
f, err := os.OpenFile(destPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
|
@ -116,36 +117,96 @@ func (m *Manager) list(includeMetadata bool) ([]extensions.Extension, error) {
|
|||
if !strings.HasPrefix(f.Name(), "gh-") {
|
||||
continue
|
||||
}
|
||||
var remoteUrl string
|
||||
updateAvailable := false
|
||||
isLocal := false
|
||||
exePath := filepath.Join(dir, f.Name(), f.Name())
|
||||
if f.IsDir() {
|
||||
if includeMetadata {
|
||||
remoteUrl = m.getRemoteUrl(f.Name())
|
||||
updateAvailable = m.checkUpdateAvailable(f.Name())
|
||||
}
|
||||
} else {
|
||||
isLocal = true
|
||||
if !isSymlink(f.Mode()) {
|
||||
// if this is a regular file, its contents is the local directory of the extension
|
||||
p, err := readPathFromFile(filepath.Join(dir, f.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exePath = filepath.Join(p, f.Name())
|
||||
}
|
||||
ext, err := m.parseExtensionDir(f, includeMetadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, &Extension{
|
||||
path: exePath,
|
||||
url: remoteUrl,
|
||||
isLocal: isLocal,
|
||||
updateAvailable: updateAvailable,
|
||||
})
|
||||
results = append(results, ext)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (m *Manager) parseExtensionDir(fi fs.FileInfo, includeMetadata bool) (*Extension, error) {
|
||||
id := m.installDir()
|
||||
if _, err := os.Stat(filepath.Join(id, fi.Name(), manifestName)); err == nil {
|
||||
return m.parseBinaryExtensionDir(fi, includeMetadata)
|
||||
}
|
||||
|
||||
return m.parseGitExtensionDir(fi, includeMetadata)
|
||||
}
|
||||
|
||||
func (m *Manager) parseBinaryExtensionDir(fi fs.FileInfo, includeMetadata bool) (*Extension, error) {
|
||||
id := m.installDir()
|
||||
exePath := filepath.Join(id, fi.Name(), fi.Name())
|
||||
manifestPath := filepath.Join(id, fi.Name(), manifestName)
|
||||
manifest, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not open %s for reading: %w", manifestPath, err)
|
||||
}
|
||||
|
||||
var bm binManifest
|
||||
err = yaml.Unmarshal(manifest, &bm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse %s: %w", manifestPath, err)
|
||||
}
|
||||
|
||||
repo := ghrepo.NewWithHost(bm.Owner, bm.Name, bm.Host)
|
||||
|
||||
var remoteURL string
|
||||
var updateAvailable bool
|
||||
|
||||
if includeMetadata {
|
||||
remoteURL = ghrepo.GenerateRepoURL(repo, "")
|
||||
var r *release
|
||||
r, err = fetchLatestRelease(m.client, repo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get release info for %s: %w", ghrepo.FullName(repo), err)
|
||||
}
|
||||
if bm.Tag != r.Tag {
|
||||
updateAvailable = true
|
||||
}
|
||||
}
|
||||
|
||||
return &Extension{
|
||||
path: exePath,
|
||||
url: remoteURL,
|
||||
updateAvailable: updateAvailable,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Manager) parseGitExtensionDir(fi fs.FileInfo, includeMetadata bool) (*Extension, error) {
|
||||
// TODO untangle local from this since local might be binary or git
|
||||
id := m.installDir()
|
||||
var remoteUrl string
|
||||
updateAvailable := false
|
||||
isLocal := false
|
||||
exePath := filepath.Join(id, fi.Name(), fi.Name())
|
||||
if fi.IsDir() {
|
||||
if includeMetadata {
|
||||
remoteUrl = m.getRemoteUrl(fi.Name())
|
||||
updateAvailable = m.checkUpdateAvailable(fi.Name())
|
||||
}
|
||||
} else {
|
||||
isLocal = true
|
||||
if !isSymlink(fi.Mode()) {
|
||||
// if this is a regular file, its contents is the local directory of the extension
|
||||
p, err := readPathFromFile(filepath.Join(id, fi.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exePath = filepath.Join(p, fi.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return &Extension{
|
||||
path: exePath,
|
||||
url: remoteUrl,
|
||||
isLocal: isLocal,
|
||||
updateAvailable: updateAvailable,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Manager) getRemoteUrl(extension string) string {
|
||||
gitExe, err := m.lookPath("git")
|
||||
if err != nil {
|
||||
|
|
@ -273,7 +334,7 @@ func (m *Manager) installBin(repo ghrepo.Interface) error {
|
|||
return fmt.Errorf("failed to serialize manifest: %w", err)
|
||||
}
|
||||
|
||||
manifestPath := filepath.Join(targetDir, "manifest.yml")
|
||||
manifestPath := filepath.Join(targetDir, manifestName)
|
||||
|
||||
f, err := os.OpenFile(manifestPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
|
|
@ -306,7 +367,7 @@ func (m *Manager) installGit(cloneURL string, stdout, stderr io.Writer) error {
|
|||
|
||||
var localExtensionUpgradeError = errors.New("local extensions can not be upgraded")
|
||||
|
||||
func (m *Manager) Upgrade(name string, force bool, stdout, stderr io.Writer) error {
|
||||
func (m *Manager) Upgrade(name string, force bool) error {
|
||||
exe, err := m.lookPath("git")
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -320,41 +381,82 @@ func (m *Manager) Upgrade(name string, force bool, stdout, stderr io.Writer) err
|
|||
someUpgraded := false
|
||||
for _, f := range exts {
|
||||
if name == "" {
|
||||
fmt.Fprintf(stdout, "[%s]: ", f.Name())
|
||||
fmt.Fprintf(m.io.Out, "[%s]: ", f.Name())
|
||||
} else if f.Name() != name {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.IsLocal() {
|
||||
if name == "" {
|
||||
fmt.Fprintf(stdout, "%s\n", localExtensionUpgradeError)
|
||||
fmt.Fprintf(m.io.Out, "%s\n", localExtensionUpgradeError)
|
||||
} else {
|
||||
err = localExtensionUpgradeError
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
var cmds []*exec.Cmd
|
||||
dir := filepath.Dir(f.Path())
|
||||
if force {
|
||||
fetchCmd := m.newCommand(exe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "fetch", "origin", "HEAD")
|
||||
resetCmd := m.newCommand(exe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "reset", "--hard", "origin/HEAD")
|
||||
cmds = []*exec.Cmd{fetchCmd, resetCmd}
|
||||
} else {
|
||||
pullCmd := m.newCommand(exe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "pull", "--ff-only")
|
||||
cmds = []*exec.Cmd{pullCmd}
|
||||
binManifestPath := filepath.Join(filepath.Dir(f.Path()), manifestName)
|
||||
if _, e := os.Stat(binManifestPath); e == nil {
|
||||
err = m.upgradeBin(f)
|
||||
someUpgraded = true
|
||||
continue
|
||||
}
|
||||
if e := runCmds(cmds, stdout, stderr); e != nil {
|
||||
|
||||
if e := m.upgradeGit(f, exe, force); e != nil {
|
||||
err = e
|
||||
}
|
||||
someUpgraded = true
|
||||
}
|
||||
|
||||
if err == nil && !someUpgraded {
|
||||
err = fmt.Errorf("no extension matched %q", name)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Manager) upgradeGit(ext extensions.Extension, exe string, force bool) error {
|
||||
var cmds []*exec.Cmd
|
||||
dir := filepath.Dir(ext.Path())
|
||||
if force {
|
||||
fetchCmd := m.newCommand(exe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "fetch", "origin", "HEAD")
|
||||
resetCmd := m.newCommand(exe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "reset", "--hard", "origin/HEAD")
|
||||
cmds = []*exec.Cmd{fetchCmd, resetCmd}
|
||||
} else {
|
||||
pullCmd := m.newCommand(exe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "pull", "--ff-only")
|
||||
cmds = []*exec.Cmd{pullCmd}
|
||||
}
|
||||
|
||||
return runCmds(cmds, m.io.Out, m.io.ErrOut)
|
||||
}
|
||||
|
||||
func (m *Manager) upgradeBin(ext extensions.Extension) error {
|
||||
manifestPath := filepath.Join(filepath.Dir(ext.Path()), manifestName)
|
||||
manifest, err := os.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open %s for reading: %w", manifestPath, err)
|
||||
}
|
||||
|
||||
var bm binManifest
|
||||
err = yaml.Unmarshal(manifest, &bm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %s: %w", manifestPath, err)
|
||||
}
|
||||
repo := ghrepo.NewWithHost(bm.Owner, bm.Name, bm.Host)
|
||||
var r *release
|
||||
|
||||
r, err = fetchLatestRelease(m.client, repo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get release info for %s: %w", ghrepo.FullName(repo), err)
|
||||
}
|
||||
|
||||
if bm.Tag == r.Tag {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.installBin(repo)
|
||||
}
|
||||
|
||||
func (m *Manager) Remove(name string) error {
|
||||
targetDir := filepath.Join(m.installDir(), "gh-"+name)
|
||||
if _, err := os.Lstat(targetDir); os.IsNotExist(err) {
|
||||
|
|
|
|||
|
|
@ -59,11 +59,59 @@ 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")))
|
||||
|
||||
assert.NoError(t, stubBinaryExtension(
|
||||
filepath.Join(tempDir, "extensions", "gh-bin-ext"),
|
||||
binManifest{
|
||||
Owner: "owner",
|
||||
Name: "gh-bin-ext",
|
||||
Host: "example.com",
|
||||
Tag: "v1.0.1",
|
||||
}))
|
||||
|
||||
m := newTestManager(tempDir, nil, nil)
|
||||
exts := m.List(false)
|
||||
assert.Equal(t, 2, len(exts))
|
||||
assert.Equal(t, "hello", exts[0].Name())
|
||||
assert.Equal(t, "two", exts[1].Name())
|
||||
assert.Equal(t, 3, len(exts))
|
||||
assert.Equal(t, "bin-ext", exts[0].Name())
|
||||
assert.Equal(t, "hello", exts[1].Name())
|
||||
assert.Equal(t, "two", exts[2].Name())
|
||||
}
|
||||
|
||||
func TestManager_List_binary_update(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
assert.NoError(t, stubBinaryExtension(
|
||||
filepath.Join(tempDir, "extensions", "gh-bin-ext"),
|
||||
binManifest{
|
||||
Owner: "owner",
|
||||
Name: "gh-bin-ext",
|
||||
Host: "example.com",
|
||||
Tag: "v1.0.1",
|
||||
}))
|
||||
|
||||
reg := httpmock.Registry{}
|
||||
defer reg.Verify(t)
|
||||
client := http.Client{Transport: ®}
|
||||
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "api/v3/repos/owner/gh-bin-ext/releases/latest"),
|
||||
httpmock.JSONResponse(
|
||||
release{
|
||||
Tag: "v1.0.2",
|
||||
Assets: []releaseAsset{
|
||||
{
|
||||
Name: "gh-bin-ext-windows-amd64",
|
||||
APIURL: "https://example.com/release/cool2",
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
m := newTestManager(tempDir, &client, nil)
|
||||
|
||||
exts := m.List(true)
|
||||
assert.Equal(t, 1, len(exts))
|
||||
assert.Equal(t, "bin-ext", exts[0].Name())
|
||||
assert.True(t, exts[0].UpdateAvailable())
|
||||
assert.Equal(t, "https://example.com/owner/gh-bin-ext", exts[0].URL())
|
||||
}
|
||||
|
||||
func TestManager_Dispatch(t *testing.T) {
|
||||
|
|
@ -108,11 +156,11 @@ 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, nil, nil)
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
err := m.Upgrade("", false, stdout, stderr)
|
||||
m := newTestManager(tempDir, nil, io)
|
||||
|
||||
err := m.Upgrade("", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, heredoc.Docf(
|
||||
|
|
@ -133,11 +181,11 @@ 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, nil, nil)
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
err := m.Upgrade("remote", false, stdout, stderr)
|
||||
m := newTestManager(tempDir, nil, io)
|
||||
|
||||
err := m.Upgrade("remote", false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, heredoc.Docf(
|
||||
`
|
||||
|
|
@ -153,11 +201,10 @@ 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, nil, nil)
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
m := newTestManager(tempDir, nil, io)
|
||||
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
err := m.Upgrade("local", false, stdout, stderr)
|
||||
err := m.Upgrade("local", false)
|
||||
assert.EqualError(t, err, "local extensions can not be upgraded")
|
||||
assert.Equal(t, "", stdout.String())
|
||||
assert.Equal(t, "", stderr.String())
|
||||
|
|
@ -170,11 +217,10 @@ func TestManager_Upgrade_Force(t *testing.T) {
|
|||
|
||||
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-remote", "gh-remote")))
|
||||
|
||||
m := newTestManager(tempDir, nil, nil)
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
m := newTestManager(tempDir, nil, io)
|
||||
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
err := m.Upgrade("remote", true, stdout, stderr)
|
||||
err := m.Upgrade("remote", true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, heredoc.Docf(
|
||||
`
|
||||
|
|
@ -192,16 +238,85 @@ func TestManager_Upgrade_Force(t *testing.T) {
|
|||
func TestManager_Upgrade_NoExtensions(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
m := newTestManager(tempDir, nil, nil)
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
m := newTestManager(tempDir, nil, io)
|
||||
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
err := m.Upgrade("", false, stdout, stderr)
|
||||
err := m.Upgrade("", false)
|
||||
assert.EqualError(t, err, "no extensions installed")
|
||||
assert.Equal(t, "", stdout.String())
|
||||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_Upgrade_BinaryExtension(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
io, _, _, _ := iostreams.Test()
|
||||
reg := httpmock.Registry{}
|
||||
defer reg.Verify(t)
|
||||
client := http.Client{Transport: ®}
|
||||
|
||||
assert.NoError(t, stubBinaryExtension(
|
||||
filepath.Join(tempDir, "extensions", "gh-bin-ext"),
|
||||
binManifest{
|
||||
Owner: "owner",
|
||||
Name: "gh-bin-ext",
|
||||
Host: "example.com",
|
||||
Tag: "v1.0.1",
|
||||
}))
|
||||
|
||||
m := newTestManager(tempDir, &client, io)
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "api/v3/repos/owner/gh-bin-ext/releases/latest"),
|
||||
httpmock.JSONResponse(
|
||||
release{
|
||||
Tag: "v1.0.2",
|
||||
Assets: []releaseAsset{
|
||||
{
|
||||
Name: "gh-bin-ext-windows-amd64",
|
||||
APIURL: "https://example.com/release/cool2",
|
||||
},
|
||||
},
|
||||
}))
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "api/v3/repos/owner/gh-bin-ext/releases/latest"),
|
||||
httpmock.JSONResponse(
|
||||
release{
|
||||
Tag: "v1.0.2",
|
||||
Assets: []releaseAsset{
|
||||
{
|
||||
Name: "gh-bin-ext-windows-amd64",
|
||||
APIURL: "https://example.com/release/cool2",
|
||||
},
|
||||
},
|
||||
}))
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "release/cool2"),
|
||||
httpmock.StringResponse("FAKE UPGRADED BINARY"))
|
||||
|
||||
err := m.Upgrade("bin-ext", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
manifest, err := os.ReadFile(filepath.Join(tempDir, "extensions/gh-bin-ext", manifestName))
|
||||
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.2",
|
||||
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 UPGRADED BINARY", string(fakeBin))
|
||||
}
|
||||
|
||||
func TestManager_Install_git(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
|
|
@ -322,7 +437,7 @@ func TestManager_Install_binary(t *testing.T) {
|
|||
err := m.Install(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
manifest, err := os.ReadFile(filepath.Join(tempDir, "extensions/gh-bin-ext/manifest.yml"))
|
||||
manifest, err := os.ReadFile(filepath.Join(tempDir, "extensions/gh-bin-ext", manifestName))
|
||||
assert.NoError(t, err)
|
||||
|
||||
var bm binManifest
|
||||
|
|
@ -401,3 +516,37 @@ func stubLocalExtension(tempDir, path string) error {
|
|||
}
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
// Given the path where an extension should be installed and a manifest struct, creates a fake binary extension on disk
|
||||
func stubBinaryExtension(installPath string, bm binManifest) error {
|
||||
if err := os.MkdirAll(installPath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
fakeBinaryPath := filepath.Join(installPath, filepath.Base(installPath))
|
||||
fb, err := os.OpenFile(fakeBinaryPath, os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fb.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bs, err := yaml.Marshal(bm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize manifest: %w", err)
|
||||
}
|
||||
|
||||
manifestPath := filepath.Join(installPath, manifestName)
|
||||
|
||||
fm, 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)
|
||||
}
|
||||
_, err = fm.Write(bs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed write manifest file: %w", err)
|
||||
}
|
||||
|
||||
return fm.Close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import (
|
|||
|
||||
//go:generate moq -rm -out extension_mock.go . Extension
|
||||
type Extension interface {
|
||||
Name() string
|
||||
Path() string
|
||||
Name() string // Extension Name without gh-
|
||||
Path() string // Path to executable
|
||||
URL() string
|
||||
IsLocal() bool
|
||||
UpdateAvailable() bool
|
||||
|
|
@ -20,7 +20,7 @@ type ExtensionManager interface {
|
|||
List(includeMetadata bool) []Extension
|
||||
Install(ghrepo.Interface) error
|
||||
InstallLocal(dir string) error
|
||||
Upgrade(name string, force bool, stdout, stderr io.Writer) error
|
||||
Upgrade(name string, force bool) error
|
||||
Remove(name string) error
|
||||
Dispatch(args []string, stdin io.Reader, stdout, stderr io.Writer) (bool, error)
|
||||
Create(name string) error
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ var _ ExtensionManager = &ExtensionManagerMock{}
|
|||
// RemoveFunc: func(name string) error {
|
||||
// panic("mock out the Remove method")
|
||||
// },
|
||||
// UpgradeFunc: func(name string, force bool, stdout io.Writer, stderr io.Writer) error {
|
||||
// UpgradeFunc: func(name string, force bool) error {
|
||||
// panic("mock out the Upgrade method")
|
||||
// },
|
||||
// }
|
||||
|
|
@ -66,7 +66,7 @@ type ExtensionManagerMock struct {
|
|||
RemoveFunc func(name string) error
|
||||
|
||||
// UpgradeFunc mocks the Upgrade method.
|
||||
UpgradeFunc func(name string, force bool, stdout io.Writer, stderr io.Writer) error
|
||||
UpgradeFunc func(name string, force bool) error
|
||||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
|
|
@ -112,10 +112,6 @@ type ExtensionManagerMock struct {
|
|||
Name string
|
||||
// Force is the force argument value.
|
||||
Force bool
|
||||
// Stdout is the stdout argument value.
|
||||
Stdout io.Writer
|
||||
// Stderr is the stderr argument value.
|
||||
Stderr io.Writer
|
||||
}
|
||||
}
|
||||
lockCreate sync.RWMutex
|
||||
|
|
@ -326,41 +322,33 @@ func (mock *ExtensionManagerMock) RemoveCalls() []struct {
|
|||
}
|
||||
|
||||
// Upgrade calls UpgradeFunc.
|
||||
func (mock *ExtensionManagerMock) Upgrade(name string, force bool, stdout io.Writer, stderr io.Writer) error {
|
||||
func (mock *ExtensionManagerMock) Upgrade(name string, force bool) error {
|
||||
if mock.UpgradeFunc == nil {
|
||||
panic("ExtensionManagerMock.UpgradeFunc: method is nil but ExtensionManager.Upgrade was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Name string
|
||||
Force bool
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Name string
|
||||
Force bool
|
||||
}{
|
||||
Name: name,
|
||||
Force: force,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
Name: name,
|
||||
Force: force,
|
||||
}
|
||||
mock.lockUpgrade.Lock()
|
||||
mock.calls.Upgrade = append(mock.calls.Upgrade, callInfo)
|
||||
mock.lockUpgrade.Unlock()
|
||||
return mock.UpgradeFunc(name, force, stdout, stderr)
|
||||
return mock.UpgradeFunc(name, force)
|
||||
}
|
||||
|
||||
// UpgradeCalls gets all the calls that were made to Upgrade.
|
||||
// Check the length with:
|
||||
// len(mockedExtensionManager.UpgradeCalls())
|
||||
func (mock *ExtensionManagerMock) UpgradeCalls() []struct {
|
||||
Name string
|
||||
Force bool
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Name string
|
||||
Force bool
|
||||
} {
|
||||
var calls []struct {
|
||||
Name string
|
||||
Force bool
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Name string
|
||||
Force bool
|
||||
}
|
||||
mock.lockUpgrade.RLock()
|
||||
calls = mock.calls.Upgrade
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue