Skip to content

Commit

Permalink
Merge pull request #176 from sol-eng/dynamic-quarto
Browse files Browse the repository at this point in the history
Dynamic quarto
  • Loading branch information
samcofer authored May 21, 2024
2 parents ac388f4 + 40e3534 commit 306e85d
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 13 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ dist/

dist/
*.log
*.sh
*.sh
*.py
5 changes: 5 additions & 0 deletions cmd/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ func TestInstallParamsValidate(t *testing.T) {
if err != nil {
t.Fatalf("failed to retrieve valid Python versions: %v", err)
}

validQuartoVersions, err := quarto.RetrieveValidQuartoVersions()
if err != nil {
t.Fatalf("error retrieving valid Quarto versions: %v", err)
}

if err != nil {
t.Fatalf("failed to retrieve valid Quarto versions: %v", err)
}
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ require (
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.2 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
golang.org/x/net v0.7.0 // indirect
Expand Down
105 changes: 94 additions & 11 deletions internal/quarto/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,98 @@ package quarto

import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/AlecAivazis/survey/v2"
"github.com/samber/lo"
"io"
"net/http"
"os"
"sort"
"strconv"
"strings"
"sync"
"time"

"github.com/AlecAivazis/survey/v2"
"github.com/samber/lo"
log "github.com/sirupsen/logrus"
"github.com/sol-eng/wbi/internal/config"
cmdlog "github.com/sol-eng/wbi/internal/logging"
"github.com/sol-eng/wbi/internal/system"
)

type Assets []struct {
BrowserDownloadURL string `json:"browser_download_url"`
}
type Quarto []struct {
Assets Assets `json:"assets"`
Name string `json:"name"`
Prerelease bool `json:"prerelease"`
}

type result struct {
index int
res http.Response
err error
}

func RetrieveValidQuartoVersions() ([]string, error) {
// TODO automate the retrieving the list of valid versions
return []string{"1.3.340", "1.2.475", "1.1.189", "1.0.38"}, nil
var availQuartoVersions []string
var results []result
var quarto Quarto
var urls []string

for pagenum := 1; pagenum < 5; pagenum++ {
urls = append(urls, "https://api.github.com/repos/quarto-dev/quarto-cli/releases?per_page=100&page="+strconv.Itoa(pagenum))
}
wg := sync.WaitGroup{}

for i, url := range urls {
wg.Add(1)

go func(i int, url string) {

res, err := http.Get(url)
if err != nil {
return
}
result := &result{i, *res, err}
results = append(results, *result)
wg.Done()

}(i, url)

}

wg.Wait()

// let's sort these results real quick
sort.Slice(results, func(i, j int) bool {
return results[i].index < results[j].index
})

for _, result := range results {

err := json.NewDecoder(result.res.Body).Decode(&quarto)
if err != nil {
return nil, err
}
for _, release := range quarto {
if release.Prerelease == false {
availQuartoVersions = append(availQuartoVersions, release.Name)
}
}
}
return availQuartoVersions, nil
}

func ValidateQuartoVersions(quartoVersions []string) error {

availQuartoVersions, err := RetrieveValidQuartoVersions()
if err != nil {
return fmt.Errorf("error retrieving valid Quarto versions: %w", err)
}

for _, quartoVersion := range quartoVersions {
if !lo.Contains(availQuartoVersions, quartoVersion) {
return errors.New("version " + quartoVersion + " is not a valid Quarto version")
Expand All @@ -49,12 +116,13 @@ func DownloadAndInstallQuarto(quartoVersion string, osType config.OperatingSyste
// Find URL
quartoURL := generateQuartoInstallURL(quartoVersion, osType)
// Download installer
installerPath, err := downloadFileQuarto(quartoURL, quartoVersion, osType)
installerPath, err := downloadFileQuarto(quartoURL, quartoVersion)
if err != nil {
return fmt.Errorf("DownloadFileQuarto: %w", err)
}
// Install Quarto
err = installQuarto(installerPath, osType, quartoVersion, true)

if err != nil {
return fmt.Errorf("InstallQuarto: %w", err)
}
Expand All @@ -71,14 +139,14 @@ func generateQuartoInstallURL(quartoVersion string, osType config.OperatingSyste
// treat RHEL 7 differently as specified here: https://docs.posit.co/resources/install-quarto/#specify-quarto-version-tar
var url string
if osType == config.Redhat7 {
url = fmt.Sprintf("https://github.com/quarto-dev/quarto-cli/releases/download/v%s/quarto-%s-linux-rhel7-amd64.tar.gz", quartoVersion, quartoVersion)
url = fmt.Sprintf("https://github.com/quarto-dev/quarto-cli/releases/download/%s/quarto-%s-linux-rhel7-amd64.tar.gz", quartoVersion, strings.Replace(quartoVersion, "v", "", -1))
} else {
url = fmt.Sprintf("https://github.com/quarto-dev/quarto-cli/releases/download/v%s/quarto-%s-linux-amd64.tar.gz", quartoVersion, quartoVersion)
url = fmt.Sprintf("https://github.com/quarto-dev/quarto-cli/releases/download/%s/quarto-%s-linux-amd64.tar.gz", quartoVersion, strings.Replace(quartoVersion, "v", "", -1))
}
return url
}

func downloadFileQuarto(url string, version string, osType config.OperatingSystem) (string, error) {
func downloadFileQuarto(url string, version string) (string, error) {
system.PrintAndLogInfo("Downloading Quarto Version: " + version + " installer from: " + url)

// Create the file
Expand All @@ -87,7 +155,16 @@ func downloadFileQuarto(url string, version string, osType config.OperatingSyste
if err != nil {
return tmpFile.Name(), err
}
defer tmpFile.Close()

defer func() {
if tempErr := tmpFile.Close(); tempErr != nil {
err = tempErr
}
}()

if err != nil {
return tmpFile.Name(), err
}

client := &http.Client{
Timeout: 30 * time.Second,
Expand All @@ -101,7 +178,12 @@ func downloadFileQuarto(url string, version string, osType config.OperatingSyste
if err != nil {
return "", errors.New("error downloading " + filename + " installer")
}
defer res.Body.Close()
defer func() {
if tempErr := res.Body.Close(); tempErr != nil {
err = tempErr
}
}()

if res.StatusCode != http.StatusOK {
return "", errors.New("error retrieving " + filename + " installer")
}
Expand All @@ -117,6 +199,7 @@ func downloadFileQuarto(url string, version string, osType config.OperatingSyste

// Installs Quarto
func installQuarto(filepath string, osType config.OperatingSystem, version string, save bool) error {

// create the /opt/quarto directory if it doesn't exist
path := fmt.Sprintf("/opt/quarto/%s", version)
if _, err := os.Stat(path); os.IsNotExist(err) {
Expand Down Expand Up @@ -180,7 +263,7 @@ func quartoLocationSymlinksPrompt(quartoPaths []string) (string, error) {
}
err := survey.AskOne(prompt, &target)
if err != nil {
return "", errors.New("there was an issue with the Quarto selection prompt for symlinking")
return "", errors.New("there was an issue with the Quarto selection prompt for symlink")
}
if target == "" {
return target, errors.New("no Quarto binary selected to be symlinked")
Expand Down
5 changes: 5 additions & 0 deletions internal/quarto/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ func ScanAndHandleQuartoVersions(osType config.OperatingSystem) error {

if quartoInstall {
// retrieve other versions and present them to the user
fmt.Printf("Retrieving available Quarto versions...")
validQuartoVersions, err := RetrieveValidQuartoVersions()
if err != nil {
return fmt.Errorf("error retrieving valid Quarto versions: %w", err)
}

if err != nil {
return fmt.Errorf("there was an issue retrieving valid Quarto versions: %w", err)
}
Expand Down

0 comments on commit 306e85d

Please sign in to comment.