list pinned exts
This commit is contained in:
parent
db53df102c
commit
bed630452b
5 changed files with 93 additions and 19 deletions
|
|
@ -65,7 +65,13 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
if !c.IsBinary() && len(version) > 8 {
|
||||
version = version[:8]
|
||||
}
|
||||
t.AddField(version, nil, nil)
|
||||
|
||||
if c.IsPinned() {
|
||||
t.AddField(version, nil, cs.Cyan)
|
||||
} else {
|
||||
t.AddField(version, nil, nil)
|
||||
}
|
||||
|
||||
var updateAvailable string
|
||||
if c.UpdateAvailable() {
|
||||
updateAvailable = "Upgrade available"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ type Extension struct {
|
|||
path string
|
||||
url string
|
||||
isLocal bool
|
||||
isPinned bool
|
||||
currentVersion string
|
||||
latestVersion string
|
||||
kind ExtensionKind
|
||||
|
|
@ -43,8 +44,13 @@ func (e *Extension) CurrentVersion() string {
|
|||
return e.currentVersion
|
||||
}
|
||||
|
||||
func (e *Extension) IsPinned() bool {
|
||||
return e.isPinned
|
||||
}
|
||||
|
||||
func (e *Extension) UpdateAvailable() bool {
|
||||
if e.isLocal ||
|
||||
if e.isPinned ||
|
||||
e.isLocal ||
|
||||
e.currentVersion == "" ||
|
||||
e.latestVersion == "" ||
|
||||
e.currentVersion == e.latestVersion {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -199,6 +198,7 @@ func (m *Manager) parseBinaryExtensionDir(fi fs.FileInfo) (Extension, error) {
|
|||
remoteURL := ghrepo.GenerateRepoURL(repo, "")
|
||||
ext.url = remoteURL
|
||||
ext.currentVersion = bm.Tag
|
||||
ext.isPinned = bm.IsPinned
|
||||
return ext, nil
|
||||
}
|
||||
|
||||
|
|
@ -207,12 +207,20 @@ func (m *Manager) parseGitExtensionDir(fi fs.FileInfo) (Extension, error) {
|
|||
exePath := filepath.Join(id, fi.Name(), fi.Name())
|
||||
remoteUrl := m.getRemoteUrl(fi.Name())
|
||||
currentVersion := m.getCurrentVersion(fi.Name())
|
||||
|
||||
var isPinned bool
|
||||
pinPath := filepath.Join(id, fi.Name(), fmt.Sprintf(".pin-%s", currentVersion))
|
||||
if _, err := os.Stat(pinPath); err == nil {
|
||||
isPinned = true
|
||||
}
|
||||
|
||||
return Extension{
|
||||
path: exePath,
|
||||
url: remoteUrl,
|
||||
isLocal: false,
|
||||
currentVersion: currentVersion,
|
||||
kind: GitKind,
|
||||
isPinned: isPinned,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -313,15 +321,16 @@ func (m *Manager) InstallLocal(dir string) error {
|
|||
}
|
||||
|
||||
type binManifest struct {
|
||||
Owner string
|
||||
Name string
|
||||
Host string
|
||||
Tag string
|
||||
Owner string
|
||||
Name string
|
||||
Host string
|
||||
Tag string
|
||||
IsPinned bool
|
||||
// TODO I may end up not using this; just thinking ahead to local installs
|
||||
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 {
|
||||
isBin, err := isBinExtension(m.client, repo)
|
||||
if err != nil {
|
||||
|
|
@ -343,13 +352,13 @@ func (m *Manager) Install(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 err error
|
||||
if targetCommitish == "" {
|
||||
r, err = fetchLatestRelease(m.client, repo)
|
||||
} else {
|
||||
isPinned := targetCommitish != ""
|
||||
if isPinned {
|
||||
r, err = fetchReleaseFromTag(m.client, repo, targetCommitish)
|
||||
} else {
|
||||
r, err = fetchLatestRelease(m.client, repo)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -372,6 +381,7 @@ func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) erro
|
|||
|
||||
name := repo.RepoName()
|
||||
targetDir := filepath.Join(m.installDir(), name)
|
||||
|
||||
// TODO clean this up if function errs?
|
||||
err = os.MkdirAll(targetDir, 0755)
|
||||
if err != nil {
|
||||
|
|
@ -387,11 +397,12 @@ func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) erro
|
|||
}
|
||||
|
||||
manifest := binManifest{
|
||||
Name: name,
|
||||
Owner: repo.RepoOwner(),
|
||||
Host: repo.RepoHost(),
|
||||
Path: binPath,
|
||||
Tag: r.Tag,
|
||||
Name: name,
|
||||
Owner: repo.RepoOwner(),
|
||||
Host: repo.RepoHost(),
|
||||
Path: binPath,
|
||||
Tag: r.Tag,
|
||||
IsPinned: isPinned,
|
||||
}
|
||||
|
||||
bs, err := yaml.Marshal(manifest)
|
||||
|
|
@ -448,9 +459,20 @@ func (m *Manager) installGit(repo ghrepo.Interface, targetCommitish string, stdo
|
|||
checkoutCmd := m.newCommand(exe, "-C", targetDir, "checkout", commitSHA)
|
||||
checkoutCmd.Stdout = stdout
|
||||
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 file 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 upToDateError = errors.New("already up to date")
|
||||
var noExtensionsInstalledError = errors.New("no extensions installed")
|
||||
|
|
@ -508,6 +530,9 @@ func (m *Manager) upgradeExtension(ext Extension, force bool) error {
|
|||
if ext.isLocal {
|
||||
return localExtensionUpgradeError
|
||||
}
|
||||
if ext.IsPinned() {
|
||||
return pinnedExtensionUpgradeError
|
||||
}
|
||||
if !ext.UpdateAvailable() {
|
||||
return upToDateError
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@ type Extension interface {
|
|||
Name() string // Extension Name without gh-
|
||||
Path() string // Path to executable
|
||||
URL() string
|
||||
IsLocal() bool
|
||||
CurrentVersion() string
|
||||
IsPinned() bool
|
||||
UpdateAvailable() bool
|
||||
IsBinary() bool
|
||||
IsLocal() bool
|
||||
}
|
||||
|
||||
//go:generate moq -rm -out manager_mock.go . ExtensionManager
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ var _ Extension = &ExtensionMock{}
|
|||
// IsLocalFunc: func() bool {
|
||||
// panic("mock out the IsLocal method")
|
||||
// },
|
||||
// IsPinnedFunc: func() bool {
|
||||
// panic("mock out the IsPinned method")
|
||||
// },
|
||||
// NameFunc: func() string {
|
||||
// panic("mock out the Name method")
|
||||
// },
|
||||
|
|
@ -54,6 +57,9 @@ type ExtensionMock struct {
|
|||
// IsLocalFunc mocks the IsLocal method.
|
||||
IsLocalFunc func() bool
|
||||
|
||||
// IsPinnedFunc mocks the IsPinned method.
|
||||
IsPinnedFunc func() bool
|
||||
|
||||
// NameFunc mocks the Name method.
|
||||
NameFunc func() string
|
||||
|
||||
|
|
@ -77,6 +83,9 @@ type ExtensionMock struct {
|
|||
// IsLocal holds details about calls to the IsLocal method.
|
||||
IsLocal []struct {
|
||||
}
|
||||
// IsPinned holds details about calls to the IsPinned method.
|
||||
IsPinned []struct {
|
||||
}
|
||||
// Name holds details about calls to the Name method.
|
||||
Name []struct {
|
||||
}
|
||||
|
|
@ -93,6 +102,7 @@ type ExtensionMock struct {
|
|||
lockCurrentVersion sync.RWMutex
|
||||
lockIsBinary sync.RWMutex
|
||||
lockIsLocal sync.RWMutex
|
||||
lockIsPinned sync.RWMutex
|
||||
lockName sync.RWMutex
|
||||
lockPath sync.RWMutex
|
||||
lockURL sync.RWMutex
|
||||
|
|
@ -177,6 +187,32 @@ func (mock *ExtensionMock) IsLocalCalls() []struct {
|
|||
return calls
|
||||
}
|
||||
|
||||
// IsPinned calls IsPinnedFunc.
|
||||
func (mock *ExtensionMock) IsPinned() bool {
|
||||
if mock.IsPinnedFunc == nil {
|
||||
panic("ExtensionMock.IsPinnedFunc: method is nil but Extension.IsPinned was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockIsPinned.Lock()
|
||||
mock.calls.IsPinned = append(mock.calls.IsPinned, callInfo)
|
||||
mock.lockIsPinned.Unlock()
|
||||
return mock.IsPinnedFunc()
|
||||
}
|
||||
|
||||
// IsPinnedCalls gets all the calls that were made to IsPinned.
|
||||
// Check the length with:
|
||||
// len(mockedExtension.IsPinnedCalls())
|
||||
func (mock *ExtensionMock) IsPinnedCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockIsPinned.RLock()
|
||||
calls = mock.calls.IsPinned
|
||||
mock.lockIsPinned.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Name calls NameFunc.
|
||||
func (mock *ExtensionMock) Name() string {
|
||||
if mock.NameFunc == nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue