88 "io"
99 "io/fs"
1010 "io/ioutil"
11- "log"
1211 "net/http"
1312 "os"
1413 "os/exec"
@@ -199,6 +198,7 @@ func (m *Manager) parseBinaryExtensionDir(fi fs.FileInfo) (Extension, error) {
199198 remoteURL := ghrepo .GenerateRepoURL (repo , "" )
200199 ext .url = remoteURL
201200 ext .currentVersion = bm .Tag
201+ ext .isPinned = bm .IsPinned
202202 return ext , nil
203203}
204204
@@ -207,12 +207,20 @@ func (m *Manager) parseGitExtensionDir(fi fs.FileInfo) (Extension, error) {
207207 exePath := filepath .Join (id , fi .Name (), fi .Name ())
208208 remoteUrl := m .getRemoteUrl (fi .Name ())
209209 currentVersion := m .getCurrentVersion (fi .Name ())
210+
211+ var isPinned bool
212+ pinPath := filepath .Join (id , fi .Name (), fmt .Sprintf (".pin-%s" , currentVersion ))
213+ if _ , err := os .Stat (pinPath ); err == nil {
214+ isPinned = true
215+ }
216+
210217 return Extension {
211218 path : exePath ,
212219 url : remoteUrl ,
213220 isLocal : false ,
214221 currentVersion : currentVersion ,
215222 kind : GitKind ,
223+ isPinned : isPinned ,
216224 }, nil
217225}
218226
@@ -313,15 +321,16 @@ func (m *Manager) InstallLocal(dir string) error {
313321}
314322
315323type binManifest struct {
316- Owner string
317- Name string
318- Host string
319- Tag string
324+ Owner string
325+ Name string
326+ Host string
327+ Tag string
328+ IsPinned bool
320329 // TODO I may end up not using this; just thinking ahead to local installs
321330 Path string
322331}
323332
324- // Install an extension from repo, and pin to commitish if provided
333+ // Install installs an extension from repo, and pins to commitish if provided
325334func (m * Manager ) Install (repo ghrepo.Interface , targetCommitish string ) error {
326335 isBin , err := isBinExtension (m .client , repo )
327336 if err != nil {
@@ -343,13 +352,13 @@ func (m *Manager) Install(repo ghrepo.Interface, targetCommitish string) error {
343352}
344353
345354func (m * Manager ) installBin (repo ghrepo.Interface , targetCommitish string ) error {
346- log .Println ("Installing binary extension" )
347355 var r * release
348356 var err error
349- if targetCommitish == "" {
350- r , err = fetchLatestRelease (m .client , repo )
351- } else {
357+ isPinned := targetCommitish != ""
358+ if isPinned {
352359 r , err = fetchReleaseFromTag (m .client , repo , targetCommitish )
360+ } else {
361+ r , err = fetchLatestRelease (m .client , repo )
353362 }
354363 if err != nil {
355364 return err
@@ -372,6 +381,7 @@ func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) erro
372381
373382 name := repo .RepoName ()
374383 targetDir := filepath .Join (m .installDir (), name )
384+
375385 // TODO clean this up if function errs?
376386 err = os .MkdirAll (targetDir , 0755 )
377387 if err != nil {
@@ -387,11 +397,12 @@ func (m *Manager) installBin(repo ghrepo.Interface, targetCommitish string) erro
387397 }
388398
389399 manifest := binManifest {
390- Name : name ,
391- Owner : repo .RepoOwner (),
392- Host : repo .RepoHost (),
393- Path : binPath ,
394- Tag : r .Tag ,
400+ Name : name ,
401+ Owner : repo .RepoOwner (),
402+ Host : repo .RepoHost (),
403+ Path : binPath ,
404+ Tag : r .Tag ,
405+ IsPinned : isPinned ,
395406 }
396407
397408 bs , err := yaml .Marshal (manifest )
@@ -448,9 +459,20 @@ func (m *Manager) installGit(repo ghrepo.Interface, targetCommitish string, stdo
448459 checkoutCmd := m .newCommand (exe , "-C" , targetDir , "checkout" , commitSHA )
449460 checkoutCmd .Stdout = stdout
450461 checkoutCmd .Stderr = stderr
451- return checkoutCmd .Run ()
462+ if err := checkoutCmd .Run (); err != nil {
463+ return err
464+ }
465+
466+ pinPath := filepath .Join (targetDir , fmt .Sprintf (".pin-%s" , commitSHA ))
467+ f , err := os .OpenFile (pinPath , os .O_WRONLY | os .O_CREATE | os .O_TRUNC , 0600 )
468+ defer f .Close ()
469+ if err != nil {
470+ return fmt .Errorf ("failed to create pin file in directory: %w" , err )
471+ }
472+ return nil
452473}
453474
475+ var pinnedExtensionUpgradeError = errors .New ("pinned extensions can not be upgraded" )
454476var localExtensionUpgradeError = errors .New ("local extensions can not be upgraded" )
455477var upToDateError = errors .New ("already up to date" )
456478var noExtensionsInstalledError = errors .New ("no extensions installed" )
@@ -508,6 +530,9 @@ func (m *Manager) upgradeExtension(ext Extension, force bool) error {
508530 if ext .isLocal {
509531 return localExtensionUpgradeError
510532 }
533+ if ext .IsPinned () {
534+ return pinnedExtensionUpgradeError
535+ }
511536 if ! ext .UpdateAvailable () {
512537 return upToDateError
513538 }
0 commit comments