list pinned exts
This commit is contained in:
parent
44334bbec6
commit
bdab7de1d2
5 changed files with 93 additions and 20 deletions
|
|
@ -61,11 +61,14 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
||||||
|
|
||||||
t.AddField(fmt.Sprintf("gh %s", c.Name()), nil, nil)
|
t.AddField(fmt.Sprintf("gh %s", c.Name()), nil, nil)
|
||||||
t.AddField(repo, nil, nil)
|
t.AddField(repo, nil, nil)
|
||||||
var updateAvailable string
|
|
||||||
if c.UpdateAvailable() {
|
if c.Pin() != "" {
|
||||||
updateAvailable = "Upgrade available"
|
t.AddField(c.Pin(), nil, cs.Cyan)
|
||||||
|
} else if c.UpdateAvailable() {
|
||||||
|
t.AddField("Upgrade available", nil, cs.Green)
|
||||||
|
} else {
|
||||||
|
t.AddField("", nil, nil)
|
||||||
}
|
}
|
||||||
t.AddField(updateAvailable, nil, cs.Green)
|
|
||||||
t.EndRow()
|
t.EndRow()
|
||||||
}
|
}
|
||||||
return t.Render()
|
return t.Render()
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ type Extension struct {
|
||||||
path string
|
path string
|
||||||
url string
|
url string
|
||||||
isLocal bool
|
isLocal bool
|
||||||
|
pin string
|
||||||
currentVersion string
|
currentVersion string
|
||||||
latestVersion string
|
latestVersion string
|
||||||
kind ExtensionKind
|
kind ExtensionKind
|
||||||
|
|
@ -39,8 +40,13 @@ func (e *Extension) IsLocal() bool {
|
||||||
return e.isLocal
|
return e.isLocal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Extension) Pin() string {
|
||||||
|
return e.pin
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Extension) UpdateAvailable() bool {
|
func (e *Extension) UpdateAvailable() bool {
|
||||||
if e.isLocal ||
|
if e.pin != "" ||
|
||||||
|
e.isLocal ||
|
||||||
e.currentVersion == "" ||
|
e.currentVersion == "" ||
|
||||||
e.latestVersion == "" ||
|
e.latestVersion == "" ||
|
||||||
e.currentVersion == e.latestVersion {
|
e.currentVersion == e.latestVersion {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -199,6 +198,9 @@ func (m *Manager) parseBinaryExtensionDir(fi fs.FileInfo) (Extension, error) {
|
||||||
remoteURL := ghrepo.GenerateRepoURL(repo, "")
|
remoteURL := ghrepo.GenerateRepoURL(repo, "")
|
||||||
ext.url = remoteURL
|
ext.url = remoteURL
|
||||||
ext.currentVersion = bm.Tag
|
ext.currentVersion = bm.Tag
|
||||||
|
if bm.Pinned {
|
||||||
|
ext.pin = bm.Tag
|
||||||
|
}
|
||||||
return ext, nil
|
return ext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,12 +209,20 @@ func (m *Manager) parseGitExtensionDir(fi fs.FileInfo) (Extension, error) {
|
||||||
exePath := filepath.Join(id, fi.Name(), fi.Name())
|
exePath := filepath.Join(id, fi.Name(), fi.Name())
|
||||||
remoteUrl := m.getRemoteUrl(fi.Name())
|
remoteUrl := m.getRemoteUrl(fi.Name())
|
||||||
currentVersion := m.getCurrentVersion(fi.Name())
|
currentVersion := m.getCurrentVersion(fi.Name())
|
||||||
|
|
||||||
|
var pinnedVersion string
|
||||||
|
pinPath := filepath.Join(id, fi.Name(), fmt.Sprintf(".pin-%s", currentVersion))
|
||||||
|
if _, err := os.Stat(pinPath); err == nil {
|
||||||
|
pinnedVersion = currentVersion[:7]
|
||||||
|
}
|
||||||
|
|
||||||
return Extension{
|
return Extension{
|
||||||
path: exePath,
|
path: exePath,
|
||||||
url: remoteUrl,
|
url: remoteUrl,
|
||||||
isLocal: false,
|
isLocal: false,
|
||||||
currentVersion: currentVersion,
|
currentVersion: currentVersion,
|
||||||
kind: GitKind,
|
kind: GitKind,
|
||||||
|
pin: pinnedVersion,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,15 +323,16 @@ func (m *Manager) InstallLocal(dir string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type binManifest struct {
|
type binManifest struct {
|
||||||
Owner string
|
Owner string
|
||||||
Name string
|
Name string
|
||||||
Host string
|
Host string
|
||||||
Tag string
|
Tag string
|
||||||
|
Pinned bool
|
||||||
// TODO I may end up not using this; just thinking ahead to local installs
|
// TODO I may end up not using this; just thinking ahead to local installs
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install an extension from repo, and pin to commitish if provided
|
// Install installs an extension from repo, and pins to commitish if provided
|
||||||
func (m *Manager) Install(repo ghrepo.Interface, targetCommitish string) error {
|
func (m *Manager) Install(repo ghrepo.Interface, targetCommitish string) error {
|
||||||
isBin, err := isBinExtension(m.client, repo)
|
isBin, err := isBinExtension(m.client, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -343,10 +354,10 @@ func (m *Manager) Install(repo ghrepo.Interface, targetCommitish string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) error {
|
func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) error {
|
||||||
log.Println("Installing binary extension")
|
|
||||||
var r *release
|
var r *release
|
||||||
var err error
|
var err error
|
||||||
if targetCommitish == "" {
|
isPinned := targetCommitish != ""
|
||||||
|
if isPinned {
|
||||||
r, err = fetchLatestRelease(m.client, repo)
|
r, err = fetchLatestRelease(m.client, repo)
|
||||||
} else {
|
} else {
|
||||||
r, err = fetchReleaseFromTag(m.client, repo, targetCommitish)
|
r, err = fetchReleaseFromTag(m.client, repo, targetCommitish)
|
||||||
|
|
@ -372,6 +383,7 @@ func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) erro
|
||||||
|
|
||||||
name := repo.RepoName()
|
name := repo.RepoName()
|
||||||
targetDir := filepath.Join(m.installDir(), name)
|
targetDir := filepath.Join(m.installDir(), name)
|
||||||
|
|
||||||
// TODO clean this up if function errs?
|
// TODO clean this up if function errs?
|
||||||
err = os.MkdirAll(targetDir, 0755)
|
err = os.MkdirAll(targetDir, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -387,11 +399,12 @@ func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest := binManifest{
|
manifest := binManifest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Owner: repo.RepoOwner(),
|
Owner: repo.RepoOwner(),
|
||||||
Host: repo.RepoHost(),
|
Host: repo.RepoHost(),
|
||||||
Path: binPath,
|
Path: binPath,
|
||||||
Tag: r.Tag,
|
Tag: r.Tag,
|
||||||
|
Pinned: isPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
bs, err := yaml.Marshal(manifest)
|
bs, err := yaml.Marshal(manifest)
|
||||||
|
|
@ -448,9 +461,20 @@ func (m *Manager) installGit(repo ghrepo.Interface, targetCommitish string, stdo
|
||||||
checkoutCmd := m.newCommand(exe, "-C", targetDir, "checkout", commitSHA)
|
checkoutCmd := m.newCommand(exe, "-C", targetDir, "checkout", commitSHA)
|
||||||
checkoutCmd.Stdout = stdout
|
checkoutCmd.Stdout = stdout
|
||||||
checkoutCmd.Stderr = stderr
|
checkoutCmd.Stderr = stderr
|
||||||
return checkoutCmd.Run()
|
if err := checkoutCmd.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pinPath := filepath.Join(targetDir, fmt.Sprintf(".pin-%s", commitSHA))
|
||||||
|
f, err := os.OpenFile(pinPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
defer f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create pin in directory: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pinnedExtensionUpgradeError = errors.New("pinned extensions can not be upgraded")
|
||||||
var localExtensionUpgradeError = errors.New("local extensions can not be upgraded")
|
var localExtensionUpgradeError = errors.New("local extensions can not be upgraded")
|
||||||
var upToDateError = errors.New("already up to date")
|
var upToDateError = errors.New("already up to date")
|
||||||
var noExtensionsInstalledError = errors.New("no extensions installed")
|
var noExtensionsInstalledError = errors.New("no extensions installed")
|
||||||
|
|
@ -508,6 +532,9 @@ func (m *Manager) upgradeExtension(ext Extension, force bool) error {
|
||||||
if ext.isLocal {
|
if ext.isLocal {
|
||||||
return localExtensionUpgradeError
|
return localExtensionUpgradeError
|
||||||
}
|
}
|
||||||
|
if ext.pin != "" {
|
||||||
|
return pinnedExtensionUpgradeError
|
||||||
|
}
|
||||||
if !ext.UpdateAvailable() {
|
if !ext.UpdateAvailable() {
|
||||||
return upToDateError
|
return upToDateError
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,10 @@ type Extension interface {
|
||||||
Name() string // Extension Name without gh-
|
Name() string // Extension Name without gh-
|
||||||
Path() string // Path to executable
|
Path() string // Path to executable
|
||||||
URL() string
|
URL() string
|
||||||
IsLocal() bool
|
Pin() string // Pinned version
|
||||||
UpdateAvailable() bool
|
UpdateAvailable() bool
|
||||||
IsBinary() bool
|
IsBinary() bool
|
||||||
|
IsLocal() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate moq -rm -out manager_mock.go . ExtensionManager
|
//go:generate moq -rm -out manager_mock.go . ExtensionManager
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ var _ Extension = &ExtensionMock{}
|
||||||
// PathFunc: func() string {
|
// PathFunc: func() string {
|
||||||
// panic("mock out the Path method")
|
// panic("mock out the Path method")
|
||||||
// },
|
// },
|
||||||
|
// PinFunc: func() string {
|
||||||
|
// panic("mock out the Pin method")
|
||||||
|
// },
|
||||||
// URLFunc: func() string {
|
// URLFunc: func() string {
|
||||||
// panic("mock out the URL method")
|
// panic("mock out the URL method")
|
||||||
// },
|
// },
|
||||||
|
|
@ -54,6 +57,9 @@ type ExtensionMock struct {
|
||||||
// PathFunc mocks the Path method.
|
// PathFunc mocks the Path method.
|
||||||
PathFunc func() string
|
PathFunc func() string
|
||||||
|
|
||||||
|
// PinFunc mocks the Pin method.
|
||||||
|
PinFunc func() string
|
||||||
|
|
||||||
// URLFunc mocks the URL method.
|
// URLFunc mocks the URL method.
|
||||||
URLFunc func() string
|
URLFunc func() string
|
||||||
|
|
||||||
|
|
@ -74,6 +80,9 @@ type ExtensionMock struct {
|
||||||
// Path holds details about calls to the Path method.
|
// Path holds details about calls to the Path method.
|
||||||
Path []struct {
|
Path []struct {
|
||||||
}
|
}
|
||||||
|
// Pin holds details about calls to the Pin method.
|
||||||
|
Pin []struct {
|
||||||
|
}
|
||||||
// URL holds details about calls to the URL method.
|
// URL holds details about calls to the URL method.
|
||||||
URL []struct {
|
URL []struct {
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +94,7 @@ type ExtensionMock struct {
|
||||||
lockIsLocal sync.RWMutex
|
lockIsLocal sync.RWMutex
|
||||||
lockName sync.RWMutex
|
lockName sync.RWMutex
|
||||||
lockPath sync.RWMutex
|
lockPath sync.RWMutex
|
||||||
|
lockPin sync.RWMutex
|
||||||
lockURL sync.RWMutex
|
lockURL sync.RWMutex
|
||||||
lockUpdateAvailable sync.RWMutex
|
lockUpdateAvailable sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
@ -193,6 +203,32 @@ func (mock *ExtensionMock) PathCalls() []struct {
|
||||||
return calls
|
return calls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pin calls PinFunc.
|
||||||
|
func (mock *ExtensionMock) Pin() string {
|
||||||
|
if mock.PinFunc == nil {
|
||||||
|
panic("ExtensionMock.PinFunc: method is nil but Extension.Pin was just called")
|
||||||
|
}
|
||||||
|
callInfo := struct {
|
||||||
|
}{}
|
||||||
|
mock.lockPin.Lock()
|
||||||
|
mock.calls.Pin = append(mock.calls.Pin, callInfo)
|
||||||
|
mock.lockPin.Unlock()
|
||||||
|
return mock.PinFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PinCalls gets all the calls that were made to Pin.
|
||||||
|
// Check the length with:
|
||||||
|
// len(mockedExtension.PinCalls())
|
||||||
|
func (mock *ExtensionMock) PinCalls() []struct {
|
||||||
|
} {
|
||||||
|
var calls []struct {
|
||||||
|
}
|
||||||
|
mock.lockPin.RLock()
|
||||||
|
calls = mock.calls.Pin
|
||||||
|
mock.lockPin.RUnlock()
|
||||||
|
return calls
|
||||||
|
}
|
||||||
|
|
||||||
// URL calls URLFunc.
|
// URL calls URLFunc.
|
||||||
func (mock *ExtensionMock) URL() string {
|
func (mock *ExtensionMock) URL() string {
|
||||||
if mock.URLFunc == nil {
|
if mock.URLFunc == nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue