Add extension upgrade --dry-run (#5098)
This commit is contained in:
parent
adf274851e
commit
2c0236d096
11 changed files with 292 additions and 107 deletions
|
|
@ -176,7 +176,7 @@ func mainRun() exitCode {
|
|||
}
|
||||
}
|
||||
}
|
||||
for _, ext := range cmdFactory.ExtensionManager.List(false) {
|
||||
for _, ext := range cmdFactory.ExtensionManager.List() {
|
||||
if strings.HasPrefix(ext.Name(), toComplete) {
|
||||
results = append(results, ext.Name())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
|
|||
return true
|
||||
}
|
||||
|
||||
for _, ext := range f.ExtensionManager.List(false) {
|
||||
for _, ext := range f.ExtensionManager.List() {
|
||||
if ext.Name() == split[0] {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func runCommand(cfg config.Config, isTTY bool, cli string, in string) (*test.Cmd
|
|||
return cfg, nil
|
||||
},
|
||||
ExtensionManager: &extensions.ExtensionManagerMock{
|
||||
ListFunc: func(bool) []extensions.Extension {
|
||||
ListFunc: func() []extensions.Extension {
|
||||
return []extensions.Extension{}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
Aliases: []string{"ls"},
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmds := m.List(true)
|
||||
cmds := m.List()
|
||||
if len(cmds) == 0 {
|
||||
return errors.New("no extensions installed")
|
||||
}
|
||||
|
|
@ -61,22 +61,13 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
|
||||
t.AddField(fmt.Sprintf("gh %s", c.Name()), nil, nil)
|
||||
t.AddField(repo, nil, nil)
|
||||
version := c.CurrentVersion()
|
||||
if !c.IsBinary() && len(version) > 8 {
|
||||
version = version[:8]
|
||||
}
|
||||
|
||||
version := displayExtensionVersion(c, c.CurrentVersion())
|
||||
if c.IsPinned() {
|
||||
t.AddField(version, nil, cs.Cyan)
|
||||
} else {
|
||||
t.AddField(version, nil, nil)
|
||||
}
|
||||
|
||||
var updateAvailable string
|
||||
if c.UpdateAvailable() {
|
||||
updateAvailable = "Upgrade available"
|
||||
}
|
||||
t.AddField(updateAvailable, nil, cs.Green)
|
||||
t.EndRow()
|
||||
}
|
||||
return t.Render()
|
||||
|
|
@ -152,6 +143,7 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
func() *cobra.Command {
|
||||
var flagAll bool
|
||||
var flagForce bool
|
||||
var flagDryRun bool
|
||||
cmd := &cobra.Command{
|
||||
Use: "upgrade {<name> | --all}",
|
||||
Short: "Upgrade installed extensions",
|
||||
|
|
@ -172,6 +164,9 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
if len(args) > 0 {
|
||||
name = normalizeExtensionSelector(args[0])
|
||||
}
|
||||
if flagDryRun {
|
||||
m.EnableDryRunMode()
|
||||
}
|
||||
cs := io.ColorScheme()
|
||||
err := m.Upgrade(name, flagForce)
|
||||
if err != nil && !errors.Is(err, upToDateError) {
|
||||
|
|
@ -186,12 +181,16 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
return cmdutil.SilentError
|
||||
}
|
||||
if io.IsStdoutTTY() {
|
||||
successStr := "Successfully"
|
||||
if flagDryRun {
|
||||
successStr = "Would have"
|
||||
}
|
||||
if errors.Is(err, upToDateError) {
|
||||
fmt.Fprintf(io.Out, "%s Extension already up to date\n", cs.SuccessIcon())
|
||||
} else if name != "" {
|
||||
fmt.Fprintf(io.Out, "%s Successfully upgraded extension %s\n", cs.SuccessIcon(), name)
|
||||
fmt.Fprintf(io.Out, "%s %s upgraded extension %s\n", cs.SuccessIcon(), successStr, name)
|
||||
} else {
|
||||
fmt.Fprintf(io.Out, "%s Successfully upgraded extensions\n", cs.SuccessIcon())
|
||||
fmt.Fprintf(io.Out, "%s %s upgraded extensions\n", cs.SuccessIcon(), successStr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -199,6 +198,7 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
}
|
||||
cmd.Flags().BoolVar(&flagAll, "all", false, "Upgrade all extensions")
|
||||
cmd.Flags().BoolVar(&flagForce, "force", false, "Force upgrade extension")
|
||||
cmd.Flags().BoolVar(&flagDryRun, "dry-run", false, "Only display upgrades")
|
||||
return cmd
|
||||
}(),
|
||||
&cobra.Command{
|
||||
|
|
@ -355,7 +355,7 @@ func checkValidExtension(rootCmd *cobra.Command, m extensions.ExtensionManager,
|
|||
return fmt.Errorf("%q matches the name of a built-in command", commandName)
|
||||
}
|
||||
|
||||
for _, ext := range m.List(false) {
|
||||
for _, ext := range m.List() {
|
||||
if ext.Name() == commandName {
|
||||
return fmt.Errorf("there is already an installed extension that provides the %q command", commandName)
|
||||
}
|
||||
|
|
@ -370,3 +370,10 @@ func normalizeExtensionSelector(n string) string {
|
|||
}
|
||||
return strings.TrimPrefix(n, "gh-")
|
||||
}
|
||||
|
||||
func displayExtensionVersion(ext extensions.Extension, version string) string {
|
||||
if !ext.IsBinary() && len(version) > 8 {
|
||||
return version[:8]
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
name: "install an extension",
|
||||
args: []string{"install", "owner/gh-some-ext"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.ListFunc = func(bool) []extensions.Extension {
|
||||
em.ListFunc = func() []extensions.Extension {
|
||||
return []extensions.Extension{}
|
||||
}
|
||||
em.InstallFunc = func(_ ghrepo.Interface, _ string) error {
|
||||
|
|
@ -60,7 +60,7 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
name: "install an extension with same name as existing extension",
|
||||
args: []string{"install", "owner/gh-existing-ext"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.ListFunc = func(bool) []extensions.Extension {
|
||||
em.ListFunc = func() []extensions.Extension {
|
||||
e := &Extension{path: "owner2/gh-existing-ext"}
|
||||
return []extensions.Extension{e}
|
||||
}
|
||||
|
|
@ -99,6 +99,12 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
wantErr: true,
|
||||
errMsg: "specify an extension to upgrade or `--all`",
|
||||
},
|
||||
{
|
||||
name: "upgrade --all with extension name error",
|
||||
args: []string{"upgrade", "test", "--all"},
|
||||
wantErr: true,
|
||||
errMsg: "cannot use `--all` with extension name",
|
||||
},
|
||||
{
|
||||
name: "upgrade an extension",
|
||||
args: []string{"upgrade", "hello"},
|
||||
|
|
@ -115,6 +121,26 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
isTTY: true,
|
||||
wantStdout: "✓ Successfully upgraded extension hello\n",
|
||||
},
|
||||
{
|
||||
name: "upgrade an extension dry run",
|
||||
args: []string{"upgrade", "hello", "--dry-run"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.EnableDryRunModeFunc = func() {}
|
||||
em.UpgradeFunc = func(name string, force bool) error {
|
||||
return nil
|
||||
}
|
||||
return func(t *testing.T) {
|
||||
dryRunCalls := em.EnableDryRunModeCalls()
|
||||
assert.Equal(t, 1, len(dryRunCalls))
|
||||
upgradeCalls := em.UpgradeCalls()
|
||||
assert.Equal(t, 1, len(upgradeCalls))
|
||||
assert.Equal(t, "hello", upgradeCalls[0].Name)
|
||||
assert.False(t, upgradeCalls[0].Force)
|
||||
}
|
||||
},
|
||||
isTTY: true,
|
||||
wantStdout: "✓ Would have upgraded extension hello\n",
|
||||
},
|
||||
{
|
||||
name: "upgrade an extension notty",
|
||||
args: []string{"upgrade", "hello"},
|
||||
|
|
@ -214,6 +240,26 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
isTTY: true,
|
||||
wantStdout: "✓ Successfully upgraded extensions\n",
|
||||
},
|
||||
{
|
||||
name: "upgrade all dry run",
|
||||
args: []string{"upgrade", "--all", "--dry-run"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.EnableDryRunModeFunc = func() {}
|
||||
em.UpgradeFunc = func(name string, force bool) error {
|
||||
return nil
|
||||
}
|
||||
return func(t *testing.T) {
|
||||
dryRunCalls := em.EnableDryRunModeCalls()
|
||||
assert.Equal(t, 1, len(dryRunCalls))
|
||||
upgradeCalls := em.UpgradeCalls()
|
||||
assert.Equal(t, 1, len(upgradeCalls))
|
||||
assert.Equal(t, "", upgradeCalls[0].Name)
|
||||
assert.False(t, upgradeCalls[0].Force)
|
||||
}
|
||||
},
|
||||
isTTY: true,
|
||||
wantStdout: "✓ Would have upgraded extensions\n",
|
||||
},
|
||||
{
|
||||
name: "upgrade all none installed",
|
||||
args: []string{"upgrade", "--all"},
|
||||
|
|
@ -313,16 +359,17 @@ func TestNewCmdExtension(t *testing.T) {
|
|||
name: "list extensions",
|
||||
args: []string{"list"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.ListFunc = func(bool) []extensions.Extension {
|
||||
ex1 := &Extension{path: "cli/gh-test", url: "https://github.com/cli/gh-test", currentVersion: "1", latestVersion: "1"}
|
||||
ex2 := &Extension{path: "cli/gh-test2", url: "https://github.com/cli/gh-test2", currentVersion: "1", latestVersion: "2"}
|
||||
em.ListFunc = func() []extensions.Extension {
|
||||
ex1 := &Extension{path: "cli/gh-test", url: "https://github.com/cli/gh-test", currentVersion: "1"}
|
||||
ex2 := &Extension{path: "cli/gh-test2", url: "https://github.com/cli/gh-test2", currentVersion: "1"}
|
||||
return []extensions.Extension{ex1, ex2}
|
||||
}
|
||||
return func(t *testing.T) {
|
||||
assert.Equal(t, 1, len(em.ListCalls()))
|
||||
calls := em.ListCalls()
|
||||
assert.Equal(t, 1, len(calls))
|
||||
}
|
||||
},
|
||||
wantStdout: "gh test\tcli/gh-test\t1\t\ngh test2\tcli/gh-test2\t1\tUpgrade available\n",
|
||||
wantStdout: "gh test\tcli/gh-test\t1\ngh test2\tcli/gh-test2\t1\n",
|
||||
},
|
||||
{
|
||||
name: "create extension interactive",
|
||||
|
|
@ -533,7 +580,7 @@ func Test_checkValidExtension(t *testing.T) {
|
|||
rootCmd.AddCommand(&cobra.Command{Use: "auth"})
|
||||
|
||||
m := &extensions.ExtensionManagerMock{
|
||||
ListFunc: func(bool) []extensions.Extension {
|
||||
ListFunc: func() []extensions.Extension {
|
||||
return []extensions.Extension{
|
||||
&extensions.ExtensionMock{
|
||||
NameFunc: func() string { return "screensaver" },
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ type Manager struct {
|
|||
client *http.Client
|
||||
config config.Config
|
||||
io *iostreams.IOStreams
|
||||
dryRunMode bool
|
||||
}
|
||||
|
||||
func NewManager(io *iostreams.IOStreams) *Manager {
|
||||
|
|
@ -64,6 +65,10 @@ func (m *Manager) SetClient(client *http.Client) {
|
|||
m.client = client
|
||||
}
|
||||
|
||||
func (m *Manager) EnableDryRunMode() {
|
||||
m.dryRunMode = true
|
||||
}
|
||||
|
||||
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")
|
||||
|
|
@ -109,8 +114,8 @@ func (m *Manager) Dispatch(args []string, stdin io.Reader, stdout, stderr io.Wri
|
|||
return true, externalCmd.Run()
|
||||
}
|
||||
|
||||
func (m *Manager) List(includeMetadata bool) []extensions.Extension {
|
||||
exts, _ := m.list(includeMetadata)
|
||||
func (m *Manager) List() []extensions.Extension {
|
||||
exts, _ := m.list(false)
|
||||
r := make([]extensions.Extension, len(exts))
|
||||
for i, v := range exts {
|
||||
val := v
|
||||
|
|
@ -384,17 +389,21 @@ func (m *Manager) installBin(repo ghrepo.Interface, target string) error {
|
|||
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)
|
||||
if !m.dryRunMode {
|
||||
err = os.MkdirAll(targetDir, 0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create installation directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
binPath := filepath.Join(targetDir, name)
|
||||
binPath += ext
|
||||
|
||||
err = downloadAsset(m.client, *asset, binPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download asset %s: %w", asset.Name, err)
|
||||
if !m.dryRunMode {
|
||||
err = downloadAsset(m.client, *asset, binPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download asset %s: %w", asset.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
manifest := binManifest{
|
||||
|
|
@ -411,17 +420,19 @@ func (m *Manager) installBin(repo ghrepo.Interface, target string) error {
|
|||
return fmt.Errorf("failed to serialize manifest: %w", err)
|
||||
}
|
||||
|
||||
manifestPath := filepath.Join(targetDir, manifestName)
|
||||
if !m.dryRunMode {
|
||||
manifestPath := filepath.Join(targetDir, manifestName)
|
||||
|
||||
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()
|
||||
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)
|
||||
_, err = f.Write(bs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed write manifest file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -519,7 +530,13 @@ func (m *Manager) upgradeExtensions(exts []Extension, force bool) error {
|
|||
fmt.Fprintf(m.io.Out, "%s\n", err)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(m.io.Out, "upgrade complete\n")
|
||||
currentVersion := displayExtensionVersion(&f, f.currentVersion)
|
||||
latestVersion := displayExtensionVersion(&f, f.latestVersion)
|
||||
if m.dryRunMode {
|
||||
fmt.Fprintf(m.io.Out, "would have upgraded from %s to %s\n", currentVersion, latestVersion)
|
||||
} else {
|
||||
fmt.Fprintf(m.io.Out, "upgraded from %s to %s\n", currentVersion, latestVersion)
|
||||
}
|
||||
}
|
||||
if failed {
|
||||
return errors.New("some extensions failed to upgrade")
|
||||
|
|
@ -548,8 +565,7 @@ func (m *Manager) upgradeExtension(ext Extension, force bool) error {
|
|||
isBin, _ = isBinExtension(m.client, repo)
|
||||
}
|
||||
if isBin {
|
||||
err = m.Remove(ext.Name())
|
||||
if err != nil {
|
||||
if err := m.Remove(ext.Name()); err != nil {
|
||||
return fmt.Errorf("failed to migrate to new precompiled extension format: %w", err)
|
||||
}
|
||||
return m.installBin(repo, "")
|
||||
|
|
@ -565,6 +581,9 @@ func (m *Manager) upgradeGitExtension(ext Extension, force bool) error {
|
|||
return err
|
||||
}
|
||||
dir := filepath.Dir(ext.path)
|
||||
if m.dryRunMode {
|
||||
return nil
|
||||
}
|
||||
if force {
|
||||
if err := m.newCommand(exe, "-C", dir, "fetch", "origin", "HEAD").Run(); err != nil {
|
||||
return err
|
||||
|
|
@ -587,6 +606,9 @@ func (m *Manager) Remove(name string) error {
|
|||
if _, err := os.Lstat(targetDir); os.IsNotExist(err) {
|
||||
return fmt.Errorf("no extension found: %q", targetDir)
|
||||
}
|
||||
if m.dryRunMode {
|
||||
return nil
|
||||
}
|
||||
return os.RemoveAll(targetDir)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,14 +87,14 @@ func TestManager_List(t *testing.T) {
|
|||
}))
|
||||
|
||||
m := newTestManager(tempDir, nil, nil)
|
||||
exts := m.List(false)
|
||||
exts := m.List()
|
||||
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) {
|
||||
func TestManager_list_includeMetadata(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
assert.NoError(t, stubBinaryExtension(
|
||||
|
|
@ -125,7 +125,8 @@ func TestManager_List_binary_update(t *testing.T) {
|
|||
|
||||
m := newTestManager(tempDir, &client, nil)
|
||||
|
||||
exts := m.List(true)
|
||||
exts, err := m.list(true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(exts))
|
||||
assert.Equal(t, "bin-ext", exts[0].Name())
|
||||
assert.True(t, exts[0].UpdateAvailable())
|
||||
|
|
@ -232,10 +233,10 @@ func TestManager_UpgradeExtensions(t *testing.T) {
|
|||
assert.Equal(t, heredoc.Docf(
|
||||
`
|
||||
[hello]: [git -C %s pull --ff-only]
|
||||
upgrade complete
|
||||
upgraded from old vers to new vers
|
||||
[local]: local extensions can not be upgraded
|
||||
[two]: [git -C %s pull --ff-only]
|
||||
upgrade complete
|
||||
upgraded from old vers to new vers
|
||||
`,
|
||||
filepath.Join(tempDir, "extensions", "gh-hello"),
|
||||
filepath.Join(tempDir, "extensions", "gh-two"),
|
||||
|
|
@ -243,6 +244,33 @@ func TestManager_UpgradeExtensions(t *testing.T) {
|
|||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_UpgradeExtensions_DryRun(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
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, stubLocalExtension(tempDir, filepath.Join(tempDir, "extensions", "gh-local", "gh-local")))
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
m := newTestManager(tempDir, nil, io)
|
||||
m.EnableDryRunMode()
|
||||
exts, err := m.list(false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(exts))
|
||||
for i := 0; i < 3; i++ {
|
||||
exts[i].currentVersion = fmt.Sprintf("%d", i)
|
||||
exts[i].latestVersion = fmt.Sprintf("%d", i+1)
|
||||
}
|
||||
err = m.upgradeExtensions(exts, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, heredoc.Doc(
|
||||
`
|
||||
[hello]: would have upgraded from 0 to 1
|
||||
[local]: local extensions can not be upgraded
|
||||
[two]: would have upgraded from 2 to 3
|
||||
`,
|
||||
), stdout.String())
|
||||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_UpgradeExtension_LocalExtension(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
assert.NoError(t, stubLocalExtension(tempDir, filepath.Join(tempDir, "extensions", "gh-local", "gh-local")))
|
||||
|
|
@ -258,6 +286,22 @@ func TestManager_UpgradeExtension_LocalExtension(t *testing.T) {
|
|||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_UpgradeExtension_LocalExtension_DryRun(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
assert.NoError(t, stubLocalExtension(tempDir, filepath.Join(tempDir, "extensions", "gh-local", "gh-local")))
|
||||
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
m := newTestManager(tempDir, nil, io)
|
||||
m.EnableDryRunMode()
|
||||
exts, err := m.list(false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(exts))
|
||||
err = m.upgradeExtension(exts[0], false)
|
||||
assert.EqualError(t, err, "local extensions can not be upgraded")
|
||||
assert.Equal(t, "", stdout.String())
|
||||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_UpgradeExtension_GitExtension(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-remote", "gh-remote")))
|
||||
|
|
@ -280,6 +324,24 @@ func TestManager_UpgradeExtension_GitExtension(t *testing.T) {
|
|||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_UpgradeExtension_GitExtension_DryRun(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-remote", "gh-remote")))
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
m := newTestManager(tempDir, nil, io)
|
||||
m.EnableDryRunMode()
|
||||
exts, err := m.list(false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(exts))
|
||||
ext := exts[0]
|
||||
ext.currentVersion = "old version"
|
||||
ext.latestVersion = "new version"
|
||||
err = m.upgradeExtension(ext, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", stdout.String())
|
||||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_UpgradeExtension_GitExtension_Force(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
extensionDir := filepath.Join(tempDir, "extensions", "gh-remote")
|
||||
|
|
@ -445,6 +507,59 @@ func TestManager_UpgradeExtension_BinaryExtension(t *testing.T) {
|
|||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_UpgradeExtension_BinaryExtension_DryRun(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
reg := httpmock.Registry{}
|
||||
defer reg.Verify(t)
|
||||
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",
|
||||
}))
|
||||
|
||||
io, _, stdout, stderr := iostreams.Test()
|
||||
m := newTestManager(tempDir, &http.Client{Transport: ®}, io)
|
||||
m.EnableDryRunMode()
|
||||
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.exe",
|
||||
APIURL: "https://example.com/release/cool2",
|
||||
},
|
||||
},
|
||||
}))
|
||||
exts, err := m.list(false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(exts))
|
||||
ext := exts[0]
|
||||
ext.latestVersion = "v1.0.2"
|
||||
err = m.upgradeExtension(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.1",
|
||||
}, bm)
|
||||
assert.Equal(t, "", stdout.String())
|
||||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_UpgradeExtension_BinaryExtension_Pinned(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ func rootHelpFunc(f *cmdutil.Factory, command *cobra.Command, args []string) {
|
|||
sort.Strings(helpTopics)
|
||||
helpEntries = append(helpEntries, helpEntry{"HELP TOPICS", strings.Join(helpTopics, "\n")})
|
||||
|
||||
if exts := f.ExtensionManager.List(false); len(exts) > 0 {
|
||||
if exts := f.ExtensionManager.List(); len(exts) > 0 {
|
||||
var names []string
|
||||
for _, ext := range exts {
|
||||
names = append(names, ext.Name())
|
||||
|
|
|
|||
|
|
@ -28,11 +28,12 @@ type Extension interface {
|
|||
|
||||
//go:generate moq -rm -out manager_mock.go . ExtensionManager
|
||||
type ExtensionManager interface {
|
||||
List(includeMetadata bool) []Extension
|
||||
List() []Extension
|
||||
Install(ghrepo.Interface, string) error
|
||||
InstallLocal(dir string) 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, tmplType ExtTemplateType) error
|
||||
EnableDryRunMode()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,6 @@ var _ Extension = &ExtensionMock{}
|
|||
// PathFunc: func() string {
|
||||
// panic("mock out the Path method")
|
||||
// },
|
||||
// PinFunc: func() string {
|
||||
// panic("mock out the Pin method")
|
||||
// },
|
||||
// URLFunc: func() string {
|
||||
// panic("mock out the URL method")
|
||||
// },
|
||||
|
|
@ -69,9 +66,6 @@ type ExtensionMock struct {
|
|||
// PathFunc mocks the Path method.
|
||||
PathFunc func() string
|
||||
|
||||
// PinFunc mocks the Pin method.
|
||||
PinFunc func() string
|
||||
|
||||
// URLFunc mocks the URL method.
|
||||
URLFunc func() string
|
||||
|
||||
|
|
@ -98,9 +92,6 @@ type ExtensionMock struct {
|
|||
// Path holds details about calls to the Path method.
|
||||
Path []struct {
|
||||
}
|
||||
// Pin holds details about calls to the Pin method.
|
||||
Pin []struct {
|
||||
}
|
||||
// URL holds details about calls to the URL method.
|
||||
URL []struct {
|
||||
}
|
||||
|
|
@ -114,7 +105,6 @@ type ExtensionMock struct {
|
|||
lockIsPinned sync.RWMutex
|
||||
lockName sync.RWMutex
|
||||
lockPath sync.RWMutex
|
||||
lockPin sync.RWMutex
|
||||
lockURL sync.RWMutex
|
||||
lockUpdateAvailable sync.RWMutex
|
||||
}
|
||||
|
|
@ -275,32 +265,6 @@ func (mock *ExtensionMock) PathCalls() []struct {
|
|||
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.
|
||||
func (mock *ExtensionMock) URL() string {
|
||||
if mock.URLFunc == nil {
|
||||
|
|
|
|||
|
|
@ -25,13 +25,16 @@ var _ ExtensionManager = &ExtensionManagerMock{}
|
|||
// DispatchFunc: func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error) {
|
||||
// panic("mock out the Dispatch method")
|
||||
// },
|
||||
// EnableDryRunModeFunc: func() {
|
||||
// panic("mock out the EnableDryRunMode method")
|
||||
// },
|
||||
// InstallFunc: func(interfaceMoqParam ghrepo.Interface, s string) error {
|
||||
// panic("mock out the Install method")
|
||||
// },
|
||||
// InstallLocalFunc: func(dir string) error {
|
||||
// panic("mock out the InstallLocal method")
|
||||
// },
|
||||
// ListFunc: func(includeMetadata bool) []Extension {
|
||||
// ListFunc: func() []Extension {
|
||||
// panic("mock out the List method")
|
||||
// },
|
||||
// RemoveFunc: func(name string) error {
|
||||
|
|
@ -53,6 +56,9 @@ type ExtensionManagerMock struct {
|
|||
// DispatchFunc mocks the Dispatch method.
|
||||
DispatchFunc func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error)
|
||||
|
||||
// EnableDryRunModeFunc mocks the EnableDryRunMode method.
|
||||
EnableDryRunModeFunc func()
|
||||
|
||||
// InstallFunc mocks the Install method.
|
||||
InstallFunc func(interfaceMoqParam ghrepo.Interface, s string) error
|
||||
|
||||
|
|
@ -60,7 +66,7 @@ type ExtensionManagerMock struct {
|
|||
InstallLocalFunc func(dir string) error
|
||||
|
||||
// ListFunc mocks the List method.
|
||||
ListFunc func(includeMetadata bool) []Extension
|
||||
ListFunc func() []Extension
|
||||
|
||||
// RemoveFunc mocks the Remove method.
|
||||
RemoveFunc func(name string) error
|
||||
|
|
@ -88,6 +94,9 @@ type ExtensionManagerMock struct {
|
|||
// Stderr is the stderr argument value.
|
||||
Stderr io.Writer
|
||||
}
|
||||
// EnableDryRunMode holds details about calls to the EnableDryRunMode method.
|
||||
EnableDryRunMode []struct {
|
||||
}
|
||||
// Install holds details about calls to the Install method.
|
||||
Install []struct {
|
||||
// InterfaceMoqParam is the interfaceMoqParam argument value.
|
||||
|
|
@ -102,8 +111,6 @@ type ExtensionManagerMock struct {
|
|||
}
|
||||
// List holds details about calls to the List method.
|
||||
List []struct {
|
||||
// IncludeMetadata is the includeMetadata argument value.
|
||||
IncludeMetadata bool
|
||||
}
|
||||
// Remove holds details about calls to the Remove method.
|
||||
Remove []struct {
|
||||
|
|
@ -118,13 +125,14 @@ type ExtensionManagerMock struct {
|
|||
Force bool
|
||||
}
|
||||
}
|
||||
lockCreate sync.RWMutex
|
||||
lockDispatch sync.RWMutex
|
||||
lockInstall sync.RWMutex
|
||||
lockInstallLocal sync.RWMutex
|
||||
lockList sync.RWMutex
|
||||
lockRemove sync.RWMutex
|
||||
lockUpgrade sync.RWMutex
|
||||
lockCreate sync.RWMutex
|
||||
lockDispatch sync.RWMutex
|
||||
lockEnableDryRunMode sync.RWMutex
|
||||
lockInstall sync.RWMutex
|
||||
lockInstallLocal sync.RWMutex
|
||||
lockList sync.RWMutex
|
||||
lockRemove sync.RWMutex
|
||||
lockUpgrade sync.RWMutex
|
||||
}
|
||||
|
||||
// Create calls CreateFunc.
|
||||
|
|
@ -205,6 +213,32 @@ func (mock *ExtensionManagerMock) DispatchCalls() []struct {
|
|||
return calls
|
||||
}
|
||||
|
||||
// EnableDryRunMode calls EnableDryRunModeFunc.
|
||||
func (mock *ExtensionManagerMock) EnableDryRunMode() {
|
||||
if mock.EnableDryRunModeFunc == nil {
|
||||
panic("ExtensionManagerMock.EnableDryRunModeFunc: method is nil but ExtensionManager.EnableDryRunMode was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
}{}
|
||||
mock.lockEnableDryRunMode.Lock()
|
||||
mock.calls.EnableDryRunMode = append(mock.calls.EnableDryRunMode, callInfo)
|
||||
mock.lockEnableDryRunMode.Unlock()
|
||||
mock.EnableDryRunModeFunc()
|
||||
}
|
||||
|
||||
// EnableDryRunModeCalls gets all the calls that were made to EnableDryRunMode.
|
||||
// Check the length with:
|
||||
// len(mockedExtensionManager.EnableDryRunModeCalls())
|
||||
func (mock *ExtensionManagerMock) EnableDryRunModeCalls() []struct {
|
||||
} {
|
||||
var calls []struct {
|
||||
}
|
||||
mock.lockEnableDryRunMode.RLock()
|
||||
calls = mock.calls.EnableDryRunMode
|
||||
mock.lockEnableDryRunMode.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Install calls InstallFunc.
|
||||
func (mock *ExtensionManagerMock) Install(interfaceMoqParam ghrepo.Interface, s string) error {
|
||||
if mock.InstallFunc == nil {
|
||||
|
|
@ -272,29 +306,24 @@ func (mock *ExtensionManagerMock) InstallLocalCalls() []struct {
|
|||
}
|
||||
|
||||
// List calls ListFunc.
|
||||
func (mock *ExtensionManagerMock) List(includeMetadata bool) []Extension {
|
||||
func (mock *ExtensionManagerMock) List() []Extension {
|
||||
if mock.ListFunc == nil {
|
||||
panic("ExtensionManagerMock.ListFunc: method is nil but ExtensionManager.List was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
IncludeMetadata bool
|
||||
}{
|
||||
IncludeMetadata: includeMetadata,
|
||||
}
|
||||
}{}
|
||||
mock.lockList.Lock()
|
||||
mock.calls.List = append(mock.calls.List, callInfo)
|
||||
mock.lockList.Unlock()
|
||||
return mock.ListFunc(includeMetadata)
|
||||
return mock.ListFunc()
|
||||
}
|
||||
|
||||
// ListCalls gets all the calls that were made to List.
|
||||
// Check the length with:
|
||||
// len(mockedExtensionManager.ListCalls())
|
||||
func (mock *ExtensionManagerMock) ListCalls() []struct {
|
||||
IncludeMetadata bool
|
||||
} {
|
||||
var calls []struct {
|
||||
IncludeMetadata bool
|
||||
}
|
||||
mock.lockList.RLock()
|
||||
calls = mock.calls.List
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue