@@ -44,7 +44,8 @@ func (m *Manager) Dispatch(args []string, stdin io.Reader, stdout, stderr io.Wri
4444 extName := args [0 ]
4545 forwardArgs := args [1 :]
4646
47- for _ , e := range m .list (false ) {
47+ exts , _ := m .list (false )
48+ for _ , e := range exts {
4849 if e .Name () == extName {
4950 exe = e .Path ()
5051 break
@@ -77,34 +78,56 @@ func (m *Manager) Dispatch(args []string, stdin io.Reader, stdout, stderr io.Wri
7778 return true , externalCmd .Run ()
7879}
7980
80- func (m * Manager ) List () []extensions.Extension {
81- return m .list (true )
81+ func (m * Manager ) List (includeMetadata bool ) []extensions.Extension {
82+ exts , _ := m .list (includeMetadata )
83+ return exts
8284}
8385
84- func (m * Manager ) list (includeMetadata bool ) []extensions.Extension {
86+ func (m * Manager ) list (includeMetadata bool ) ( []extensions.Extension , error ) {
8587 dir := m .installDir ()
8688 entries , err := ioutil .ReadDir (dir )
8789 if err != nil {
88- return nil
90+ return nil , err
8991 }
92+
9093 var results []extensions.Extension
9194 for _ , f := range entries {
92- if ! strings .HasPrefix (f .Name (), "gh-" ) || ! ( f . IsDir () || f . Mode () & os . ModeSymlink != 0 ) {
95+ if ! strings .HasPrefix (f .Name (), "gh-" ) {
9396 continue
9497 }
9598 var remoteUrl string
96- var updateAvailable bool
97- if includeMetadata {
98- remoteUrl = m .getRemoteUrl (f .Name ())
99- updateAvailable = m .checkUpdateAvailable (f .Name ())
99+ updateAvailable := false
100+ isLocal := false
101+ exePath := filepath .Join (dir , f .Name (), f .Name ())
102+ if f .IsDir () {
103+ if includeMetadata {
104+ remoteUrl = m .getRemoteUrl (f .Name ())
105+ updateAvailable = m .checkUpdateAvailable (f .Name ())
106+ }
107+ } else {
108+ isLocal = true
109+ if f .Mode ()& os .ModeSymlink == 0 {
110+ // if this is a regular file, its contents is the local directory of the extension
111+ exeFile , err := os .Open (filepath .Join (dir , f .Name ()))
112+ if err != nil {
113+ return nil , err
114+ }
115+ b := make ([]byte , 1024 )
116+ n , err := exeFile .Read (b )
117+ if err != nil {
118+ return nil , err
119+ }
120+ exePath = filepath .Join (strings .TrimSpace (string (b [:n ])), f .Name ())
121+ }
100122 }
101123 results = append (results , & Extension {
102- path : filepath . Join ( dir , f . Name (), f . Name ()) ,
124+ path : exePath ,
103125 url : remoteUrl ,
126+ isLocal : isLocal ,
104127 updateAvailable : updateAvailable ,
105128 })
106129 }
107- return results
130+ return results , nil
108131}
109132
110133func (m * Manager ) getRemoteUrl (extension string ) string {
@@ -146,8 +169,19 @@ func (m *Manager) checkUpdateAvailable(extension string) bool {
146169
147170func (m * Manager ) InstallLocal (dir string ) error {
148171 name := filepath .Base (dir )
149- targetDir := filepath .Join (m .installDir (), name )
150- return os .Symlink (dir , targetDir )
172+ targetLink := filepath .Join (m .installDir (), name )
173+ if err := os .MkdirAll (filepath .Dir (targetLink ), 0755 ); err != nil {
174+ return err
175+ }
176+ // Create a regular file that contains the location of the directory where to find this extension. We
177+ // avoid relying on symlinks because creating them on Windows requires administrator privileges.
178+ f , err := os .OpenFile (targetLink , os .O_WRONLY | os .O_CREATE , 0644 )
179+ if err != nil {
180+ return err
181+ }
182+ defer f .Close ()
183+ _ , err = f .WriteString (dir )
184+ return err
151185}
152186
153187func (m * Manager ) Install (cloneURL string , stdout , stderr io.Writer ) error {
@@ -173,7 +207,7 @@ func (m *Manager) Upgrade(name string, force bool, stdout, stderr io.Writer) err
173207 return err
174208 }
175209
176- exts := m .List ()
210+ exts := m .List (false )
177211 if len (exts ) == 0 {
178212 return errors .New ("no extensions installed" )
179213 }
0 commit comments