This repository has been archived on 2024-03-03. You can view files and clone it, but cannot push or open issues or pull requests.
doggo-fetcher/internal/release.go

189 lines
3.7 KiB
Go

package internal
import (
"archive/tar"
"compress/gzip"
"errors"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/briandowns/spinner"
)
type ReleaseActions interface {
DownloadRelease() error
ExtractRelease() error
CheckReleaseExists() error
GetReleaseFolder() string
downloadFile(*os.File) error
}
type Release struct {
release string
releaseFolder string
client *http.Client
archivePath string
}
// NewRelease returns a Release object
func NewRelease(release, releaseFolder string) ReleaseActions {
return &Release{
release: release,
releaseFolder: releaseFolder,
client: http.DefaultClient,
}
}
// DownloadRelease downloads the golang release
func (d *Release) DownloadRelease() error {
d.archivePath = fmt.Sprintf("%s/%s.tar.gz", os.TempDir(), d.release)
f, err := os.Create(d.archivePath)
if err != nil {
return err
}
defer f.Close()
return d.downloadFile(f)
}
// downloadFile downloads the file to the given writer
func (d *Release) downloadFile(f *os.File) error {
s := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
s.Suffix = " Downloading release..."
s.Start()
defer s.Stop()
rsp, err := d.client.Get(fmt.Sprintf("%s/%s.%s-amd64.tar.gz", GO_DL_SERVER, d.release, runtime.GOOS))
if err != nil {
return err
}
defer rsp.Body.Close()
if rsp.StatusCode != 200 {
return errors.New("golang download server return a non success status code")
}
if _, err := io.Copy(f, rsp.Body); err != nil {
return err
}
return nil
}
// ExtractRelease extracts the golang release
func (d *Release) ExtractRelease() error {
f, err := os.Open(d.archivePath)
if err != nil {
return err
}
defer f.Close()
gzipReader, err := gzip.NewReader(f)
if err != nil {
return err
}
defer gzipReader.Close()
tarReader := tar.NewReader(gzipReader)
if err := os.MkdirAll(d.releaseFolder, 0755); err != nil {
return err
}
var rootFolder string
start := true
for {
h, err := tarReader.Next()
if err != nil {
if err == io.EOF {
return os.Remove(d.archivePath)
}
return err
}
// skip first folder
if start {
rootFolder = strings.Split(h.Name, string(os.PathSeparator))[0]
start = false
if h.Typeflag == tar.TypeDir {
continue
}
}
target := filepath.Join(d.releaseFolder, strings.Replace(h.Name, rootFolder, "", 1))
switch h.Typeflag {
case tar.TypeDir:
if err = os.MkdirAll(target, h.FileInfo().Mode()); err != nil {
return err
}
case tar.TypeReg:
file, err := os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, h.FileInfo().Mode())
if err != nil {
// for 1.21.xx, folder aren't detected as tar.TypeDir
if os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
return err
}
file, err = os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, h.FileInfo().Mode())
if err != nil {
return err
}
} else {
return err
}
}
if _, err = io.Copy(file, tarReader); err != nil {
file.Close()
return err
}
file.Close()
}
}
}
// CheckReleaseExists checks if the release exists in doggofetcher folder
func (d *Release) CheckReleaseExists() error {
fi, err := os.Stat(d.releaseFolder)
if err != nil {
if os.IsNotExist(err) {
return ErrReleaseNotFound
}
return err
}
if !fi.IsDir() {
return ErrReleaseNotFound
}
entries, err := os.ReadDir(d.releaseFolder)
if err != nil {
return err
}
if len(entries) == 0 {
if err := os.RemoveAll(d.releaseFolder); err != nil {
return err
}
return ErrReleaseNotFound
}
return nil
}
// GetReleaseFolder returns the release folder
func (d *Release) GetReleaseFolder() string {
return d.releaseFolder
}