@@ -417,58 +417,81 @@ func (m *Manager) installGit(cloneURL string, stdout, stderr io.Writer) error {
417417}
418418
419419var localExtensionUpgradeError = errors .New ("local extensions can not be upgraded" )
420+ var upToDateError = errors .New ("already up to date" )
421+ var noExtensionsInstalledError = errors .New ("no extensions installed" )
420422
421423func (m * Manager ) Upgrade (name string , force bool ) error {
422- exe , err := m .lookPath ("git" )
423- if err != nil {
424- return err
425- }
426-
427- exts := m .List (false )
424+ // Fetch metadata during list only when upgrading all extensions.
425+ // This is a performance improvement so that we don't make a
426+ // bunch of unecessary network requests when trying to upgrade a single extension.
427+ fetchMetadata := name == ""
428+ exts , _ := m .list (fetchMetadata )
428429 if len (exts ) == 0 {
429- return errors .New ("no extensions installed" )
430+ return noExtensionsInstalledError
431+ }
432+ if name == "" {
433+ return m .upgradeExtensions (exts , force )
430434 }
431-
432- someUpgraded := false
433435 for _ , f := range exts {
434- if name == "" {
435- fmt .Fprintf (m .io .Out , "[%s]: " , f .Name ())
436- } else if f .Name () != name {
436+ if f .Name () != name {
437437 continue
438438 }
439-
440- if f .IsLocal () {
441- if name == "" {
442- fmt .Fprintf (m .io .Out , "%s\n " , localExtensionUpgradeError )
443- } else {
444- err = localExtensionUpgradeError
445- }
446- continue
439+ var err error
440+ // For single extensions manually retrieve latest version since we forgo
441+ // doing it during list.
442+ f .latestVersion , err = m .getLatestVersion (f )
443+ if err != nil {
444+ return err
447445 }
446+ return m .upgradeExtension (f , force )
447+ }
448+ return fmt .Errorf ("no extension matched %q" , name )
449+ }
448450
449- binManifestPath := filepath .Join (filepath .Dir (f .Path ()), manifestName )
450- if _ , e := os .Stat (binManifestPath ); e == nil {
451- err = m .upgradeBin (f )
452- someUpgraded = true
451+ func (m * Manager ) upgradeExtensions (exts []Extension , force bool ) error {
452+ var failed bool
453+ for _ , f := range exts {
454+ fmt .Fprintf (m .io .Out , "[%s]: " , f .Name ())
455+ err := m .upgradeExtension (f , force )
456+ if err != nil {
457+ if ! errors .Is (err , localExtensionUpgradeError ) &&
458+ ! errors .Is (err , upToDateError ) {
459+ failed = true
460+ }
461+ fmt .Fprintf (m .io .Out , "%s\n " , err )
453462 continue
454463 }
455-
456- if e := m .upgradeGit (f , exe , force ); e != nil {
457- err = e
458- }
459- someUpgraded = true
464+ fmt .Fprintf (m .io .Out , "upgrade complete\n " )
460465 }
461-
462- if err == nil && ! someUpgraded {
463- err = fmt .Errorf ("no extension matched %q" , name )
466+ if failed {
467+ return errors .New ("some extensions failed to upgrade" )
464468 }
469+ return nil
470+ }
465471
472+ func (m * Manager ) upgradeExtension (ext Extension , force bool ) error {
473+ if ext .isLocal {
474+ return localExtensionUpgradeError
475+ }
476+ if ! ext .UpdateAvailable () {
477+ return upToDateError
478+ }
479+ var err error
480+ if ext .kind == BinaryKind {
481+ err = m .upgradeBinExtension (ext )
482+ } else {
483+ err = m .upgradeGitExtension (ext , force )
484+ }
466485 return err
467486}
468487
469- func (m * Manager ) upgradeGit (ext extensions.Extension , exe string , force bool ) error {
488+ func (m * Manager ) upgradeGitExtension (ext Extension , force bool ) error {
489+ exe , err := m .lookPath ("git" )
490+ if err != nil {
491+ return err
492+ }
470493 var cmds []* exec.Cmd
471- dir := filepath .Dir (ext .Path () )
494+ dir := filepath .Dir (ext .path )
472495 if force {
473496 fetchCmd := m .newCommand (exe , "-C" , dir , "--git-dir=" + filepath .Join (dir , ".git" ), "fetch" , "origin" , "HEAD" )
474497 resetCmd := m .newCommand (exe , "-C" , dir , "--git-dir=" + filepath .Join (dir , ".git" ), "reset" , "--hard" , "origin/HEAD" )
@@ -477,34 +500,14 @@ func (m *Manager) upgradeGit(ext extensions.Extension, exe string, force bool) e
477500 pullCmd := m .newCommand (exe , "-C" , dir , "--git-dir=" + filepath .Join (dir , ".git" ), "pull" , "--ff-only" )
478501 cmds = []* exec.Cmd {pullCmd }
479502 }
480-
481- return runCmds (cmds , m .io .Out , m .io .ErrOut )
503+ return runCmds (cmds )
482504}
483505
484- func (m * Manager ) upgradeBin (ext extensions.Extension ) error {
485- manifestPath := filepath .Join (filepath .Dir (ext .Path ()), manifestName )
486- manifest , err := os .ReadFile (manifestPath )
506+ func (m * Manager ) upgradeBinExtension (ext Extension ) error {
507+ repo , err := ghrepo .FromFullName (ext .url )
487508 if err != nil {
488- return fmt .Errorf ("could not open %s for reading : %w" , manifestPath , err )
509+ return fmt .Errorf ("failed to parse URL %s : %w" , ext . url , err )
489510 }
490-
491- var bm binManifest
492- err = yaml .Unmarshal (manifest , & bm )
493- if err != nil {
494- return fmt .Errorf ("could not parse %s: %w" , manifestPath , err )
495- }
496- repo := ghrepo .NewWithHost (bm .Owner , bm .Name , bm .Host )
497- var r * release
498-
499- r , err = fetchLatestRelease (m .client , repo )
500- if err != nil {
501- return fmt .Errorf ("failed to get release info for %s: %w" , ghrepo .FullName (repo ), err )
502- }
503-
504- if bm .Tag == r .Tag {
505- return nil
506- }
507-
508511 return m .installBin (repo )
509512}
510513
@@ -590,10 +593,8 @@ func (m *Manager) Create(name string) error {
590593 return err
591594}
592595
593- func runCmds (cmds []* exec.Cmd , stdout , stderr io. Writer ) error {
596+ func runCmds (cmds []* exec.Cmd ) error {
594597 for _ , cmd := range cmds {
595- cmd .Stdout = stdout
596- cmd .Stderr = stderr
597598 if err := cmd .Run (); err != nil {
598599 return err
599600 }
0 commit comments