Skip to content

Commit

Permalink
#55 handle empty package dir gracefully
Browse files Browse the repository at this point in the history
  • Loading branch information
iignatevich committed Dec 24, 2024
1 parent 3910dab commit 120b365
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 14 deletions.
51 changes: 45 additions & 6 deletions compose/downloadManager.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package compose

import (
"context"
"fmt"
"io"
"os"
"os/signal"
"path/filepath"
"syscall"

"github.com/launchrctl/launchr"
)
Expand All @@ -17,7 +21,7 @@ const (

// Downloader interface
type Downloader interface {
Download(pkg *Package, targetDir string) error
Download(ctx context.Context, pkg *Package, targetDir string) error
EnsureLatest(pkg *Package, downloadPath string) (bool, error)
}

Expand Down Expand Up @@ -132,11 +136,32 @@ func downloadPackage(pkg *Package, targetDir string, kw *keyringWrapper) error {
downloadPath = packagePath
}

err = downloader.Download(pkg, downloadPath)
if err != nil {
errRemove := os.RemoveAll(downloadPath)
if errRemove != nil {
launchr.Log().Debug("error cleaning package folder", "path", downloadPath, "err", err)
// Create a context that gets canceled on OS signals
ctx, cancel := context.WithCancel(context.Background())

// Channel to listen for OS signals
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)

// Goroutine to handle OS signals
go func() {
<-signalChan
fmt.Println("\nTermination signal received. Cleaning up...")
cancel()
}()

select {
case <-ctx.Done():
launchr.Term().Printfln("111")
fmt.Println("Download interrupted. Exiting...")
return ctx.Err()
default:
err = downloader.Download(ctx, pkg, downloadPath)
if err != nil {
errRemove := os.RemoveAll(downloadPath)
if errRemove != nil {
launchr.Log().Debug("error cleaning package folder", "path", downloadPath, "err", err)
}
}
}

Expand All @@ -156,5 +181,19 @@ func IsEmptyDir(name string) (bool, error) {
return true, nil
}

// Check if .git exists and nothing else
gitPath := filepath.Join(name, ".git")
if _, err = os.Stat(gitPath); err == nil {
// .git exists, now check if it's the only entry
entries, err := f.Readdirnames(2) // Read at most 2 entries
if err != nil {
return false, err
}
if len(entries) == 1 && entries[0] == ".git" {
return true, nil
}
}

// Directory is not empty
return false, err
}
16 changes: 9 additions & 7 deletions compose/git.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package compose

import (
"context"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -241,7 +242,7 @@ func (g *gitDownloader) ensureLatestTag(r *git.Repository, fetchURL, refName str
}

// Download implements Downloader.Download interface
func (g *gitDownloader) Download(pkg *Package, targetDir string) error {
func (g *gitDownloader) Download(ctx context.Context, pkg *Package, targetDir string) error {
launchr.Term().Printfln("git fetch: %s", pkg.GetURL())

url := pkg.GetURL()
Expand All @@ -252,7 +253,7 @@ func (g *gitDownloader) Download(pkg *Package, targetDir string) error {
ref := pkg.GetRef()
if ref == "" {
// Try to clone latest master branch.
err := g.tryDownload(targetDir, g.buildOptions(url))
err := g.tryDownload(ctx, targetDir, g.buildOptions(url))
if err != nil {
return err
}
Expand All @@ -268,7 +269,7 @@ func (g *gitDownloader) Download(pkg *Package, targetDir string) error {
options := g.buildOptions(url)
options.ReferenceName = r

err := g.tryDownload(targetDir, options)
err := g.tryDownload(ctx, targetDir, options)
if err != nil {
noMatchError := git.NoMatchingRefSpecError{}
if errors.Is(err, noMatchError) {
Expand Down Expand Up @@ -297,12 +298,13 @@ func (g *gitDownloader) buildOptions(url string) *git.CloneOptions {
}
}

func (g *gitDownloader) tryDownload(targetDir string, options *git.CloneOptions) error {
func (g *gitDownloader) tryDownload(ctx context.Context, targetDir string, options *git.CloneOptions) error {
url := options.URL
auths := []authorizationMode{authorisationNone, authorisationKeyring, authorisationManual}
for _, authType := range auths {
if authType == authorisationNone {
_, err := git.PlainClone(targetDir, false, options)
_, err := git.PlainCloneContext(ctx, targetDir, false, options)
launchr.Term().Println("")
if err != nil {
if errors.Is(err, transport.ErrAuthenticationRequired) {
launchr.Term().Println("auth required, trying keyring authorisation")
Expand All @@ -324,7 +326,7 @@ func (g *gitDownloader) tryDownload(targetDir string, options *git.CloneOptions)
Password: ci.Password,
}

_, err = git.PlainClone(targetDir, false, options)
_, err = git.PlainCloneContext(ctx, targetDir, false, options)
if err != nil {
if errors.Is(err, transport.ErrAuthorizationFailed) || errors.Is(err, transport.ErrAuthenticationRequired) {
if g.k.interactive {
Expand All @@ -350,7 +352,7 @@ func (g *gitDownloader) tryDownload(targetDir string, options *git.CloneOptions)
Password: ci.Password,
}

_, err = git.PlainClone(targetDir, false, options)
_, err = git.PlainCloneContext(ctx, targetDir, false, options)
if err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion compose/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"archive/tar"
"archive/zip"
"compress/gzip"
"context"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -51,7 +52,7 @@ func (h *httpDownloader) EnsureLatest(_ *Package, downloadPath string) (bool, er
}

// Download implements Downloader.Download interface
func (h *httpDownloader) Download(pkg *Package, targetDir string) error {
func (h *httpDownloader) Download(_ context.Context, pkg *Package, targetDir string) error {
url := pkg.GetURL()
name := rgxNameFromURL.FindString(url)
if name == "" {
Expand Down

0 comments on commit 120b365

Please sign in to comment.