Skip to content

Commit

Permalink
feat: headless batch install (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
marwanhawari authored Apr 5, 2024
1 parent 612bece commit ce007fb
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 231 deletions.
2 changes: 1 addition & 1 deletion cmd/browse.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func Browse(cliInput string) {
stew.CatchAndExit(err)
fmt.Printf("✅ Downloaded %v to %v\n", constants.GreenColor(asset), constants.GreenColor(stewPkgPath))

binaryName, err := stew.InstallBinary(downloadPath, repo, systemInfo, &lockFile, false)
binaryName, err := stew.InstallBinary(downloadPath, repo, systemInfo, &lockFile, false, "")
if err != nil {
os.RemoveAll(downloadPath)
stew.CatchAndExit(err)
Expand Down
256 changes: 149 additions & 107 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,148 +4,190 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/marwanhawari/stew/constants"
stew "github.com/marwanhawari/stew/lib"
)

// Install is executed when you run `stew install`
func Install(cliInputs []string) {
var err error

func Install(cliInput string) {
userOS, userArch, _, systemInfo, err := stew.Initialize()
stew.CatchAndExit(err)

for _, cliInput := range cliInputs {
if strings.Contains(cliInput, "Stewfile.lock.json") {
cliInputs, err = stew.ReadStewLockFileContents(cliInput)
stew.CatchAndExit(err)
break
if filepath.Base(cliInput) == "Stewfile.lock.json" {
pkgs, err := stew.ReadStewLockFileContents(cliInput)
stew.CatchAndExit(err)
if len(pkgs) == 0 {
stew.CatchAndExit(stew.EmptyCLIInputError{})
}

if strings.Contains(cliInput, "Stewfile") {
cliInputs, err = stew.ReadStewfileContents(cliInput)
stew.CatchAndExit(err)
break
err = installFromLockFile(pkgs, userOS, userArch, systemInfo)
stew.CatchAndExit(err)
} else if filepath.Base(cliInput) == "Stewfile" {
pkgs, err := stew.ReadStewfileContents(cliInput)
stew.CatchAndExit(err)
if len(pkgs) == 0 {
stew.CatchAndExit(stew.EmptyCLIInputError{})
}
installFromStewfile(pkgs, userOS, userArch, systemInfo)
} else {
pkg, err := stew.ParseCLIInput(cliInput)
stew.CatchAndExit(err)
err = installOne(pkg, userOS, userArch, systemInfo, false)
stew.CatchAndExit(err)
}
}

if len(cliInputs) == 0 {
stew.CatchAndExit(stew.EmptyCLIInputError{})
func installOne(pkg stew.PackageData, userOS, userArch string, systemInfo stew.SystemInfo, installingFromLockFile bool) error {
sp := constants.LoadingSpinner

stewBinPath := systemInfo.StewBinPath
stewPkgPath := systemInfo.StewPkgPath
stewLockFilePath := systemInfo.StewLockFilePath
stewTmpPath := systemInfo.StewTmpPath

source := pkg.Source
owner := pkg.Owner
repo := pkg.Repo
tag := pkg.Tag
asset := pkg.Asset
desiredBinaryRename := pkg.Binary
downloadURL := pkg.URL

lockFile, err := stew.NewLockFile(stewLockFilePath, userOS, userArch)
if err != nil {
return err
}

for _, cliInput := range cliInputs {
sp := constants.LoadingSpinner

stewBinPath := systemInfo.StewBinPath
stewPkgPath := systemInfo.StewPkgPath
stewLockFilePath := systemInfo.StewLockFilePath
stewTmpPath := systemInfo.StewTmpPath

parsedInput, err := stew.ParseCLIInput(cliInput)
stew.CatchAndExit(err)

owner := parsedInput.Owner
repo := parsedInput.Repo
tag := parsedInput.Tag
asset := parsedInput.Asset
downloadURL := parsedInput.DownloadURL

lockFile, err := stew.NewLockFile(stewLockFilePath, userOS, userArch)
stew.CatchAndExit(err)
if err = os.RemoveAll(stewTmpPath); err != nil {
return err
}
if err = os.MkdirAll(stewTmpPath, 0755); err != nil {
return err
}

err = os.RemoveAll(stewTmpPath)
stew.CatchAndExit(err)
err = os.MkdirAll(stewTmpPath, 0755)
stew.CatchAndExit(err)
var githubProject stew.GithubProject
if source == "github" {
fmt.Println(constants.GreenColor(owner + "/" + repo))
sp.Start()
githubProject, err = stew.NewGithubProject(owner, repo)
sp.Stop()
if err != nil {
return err
}

var githubProject stew.GithubProject
if parsedInput.IsGithubInput {
fmt.Println(constants.GreenColor(owner + "/" + repo))
sp.Start()
githubProject, err = stew.NewGithubProject(owner, repo)
sp.Stop()
stew.CatchAndExit(err)
releaseTags, err := stew.GetGithubReleasesTags(githubProject)
if err != nil {
return err
}

// This will make sure that there are any tags at all
releaseTags, err := stew.GetGithubReleasesTags(githubProject)
stew.CatchAndExit(err)
if tag == "" || tag == "latest" {
tag = githubProject.Releases[0].TagName
}

if tag == "" || tag == "latest" {
tag = githubProject.Releases[0].TagName
tagIndex, tagFound := stew.Contains(releaseTags, tag)
if !tagFound {
tag, err = stew.WarningPromptSelect(fmt.Sprintf("Could not find a release with the tag %v - please select a release:", constants.YellowColor(tag)), releaseTags)
if err != nil {
return err
}
tagIndex, _ = stew.Contains(releaseTags, tag)
}

// Need to make sure user input tag is in the tags
tagIndex, tagFound := stew.Contains(releaseTags, tag)
if !tagFound {
tag, err = stew.WarningPromptSelect(fmt.Sprintf("Could not find a release with the tag %v - please select a release:", constants.YellowColor(tag)), releaseTags)
stew.CatchAndExit(err)
tagIndex, _ = stew.Contains(releaseTags, tag)
}
releaseAssets, err := stew.GetGithubReleasesAssets(githubProject, tag)
if err != nil {
return err
}

// Make sure there are any assets at all
releaseAssets, err := stew.GetGithubReleasesAssets(githubProject, tag)
stew.CatchAndExit(err)
if asset == "" {
asset, err = stew.DetectAsset(userOS, userArch, releaseAssets)
}
if err != nil {
return err
}

if asset == "" {
asset, err = stew.DetectAsset(userOS, userArch, releaseAssets)
assetIndex, assetFound := stew.Contains(releaseAssets, asset)
if !assetFound {
asset, err = stew.WarningPromptSelect(fmt.Sprintf("Could not find the asset %v - please select an asset:", constants.YellowColor(asset)), releaseAssets)
if err != nil {
return err
}
stew.CatchAndExit(err)
assetIndex, _ = stew.Contains(releaseAssets, asset)
}

assetIndex, assetFound := stew.Contains(releaseAssets, asset)
if !assetFound {
asset, err = stew.WarningPromptSelect(fmt.Sprintf("Could not find the asset %v - please select an asset:", constants.YellowColor(asset)), releaseAssets)
stew.CatchAndExit(err)
assetIndex, _ = stew.Contains(releaseAssets, asset)
}
downloadURL = githubProject.Releases[tagIndex].Assets[assetIndex].DownloadURL
} else {
fmt.Println(constants.GreenColor(asset))
}

downloadURL = githubProject.Releases[tagIndex].Assets[assetIndex].DownloadURL
downloadPath := filepath.Join(stewPkgPath, asset)
err = stew.DownloadFile(downloadPath, downloadURL)
if err != nil {
return err
}
fmt.Printf("✅ Downloaded %v to %v\n", constants.GreenColor(asset), constants.GreenColor(stewPkgPath))

} else {
fmt.Println(constants.GreenColor(asset))
binaryName, err := stew.InstallBinary(downloadPath, repo, systemInfo, &lockFile, installingFromLockFile, desiredBinaryRename)
if err != nil {
if err := os.RemoveAll(downloadPath); err != nil {
return err
}
}

downloadPath := filepath.Join(stewPkgPath, asset)
err = stew.DownloadFile(downloadPath, downloadURL)
stew.CatchAndExit(err)
fmt.Printf("✅ Downloaded %v to %v\n", constants.GreenColor(asset), constants.GreenColor(stewPkgPath))

binaryName, err := stew.InstallBinary(downloadPath, repo, systemInfo, &lockFile, false)
if err != nil {
os.RemoveAll(downloadPath)
stew.CatchAndExit(err)
var packageData stew.PackageData
if source == "github" {
packageData = stew.PackageData{
Source: "github",
Owner: githubProject.Owner,
Repo: githubProject.Repo,
Tag: tag,
Asset: asset,
Binary: binaryName,
URL: downloadURL,
}

var packageData stew.PackageData
if parsedInput.IsGithubInput {
packageData = stew.PackageData{
Source: "github",
Owner: githubProject.Owner,
Repo: githubProject.Repo,
Tag: tag,
Asset: asset,
Binary: binaryName,
URL: downloadURL,
}
} else {
packageData = stew.PackageData{
Source: "other",
Owner: "",
Repo: "",
Tag: "",
Asset: asset,
Binary: binaryName,
URL: downloadURL,
}
} else {
packageData = stew.PackageData{
Source: "other",
Owner: "",
Repo: "",
Tag: "",
Asset: asset,
Binary: binaryName,
URL: downloadURL,
}
}

indexInLockFile, binaryFoundInLockFile := stew.FindBinaryInLockFile(lockFile, binaryName)
if installingFromLockFile && binaryFoundInLockFile {
lockFile.Packages[indexInLockFile] = packageData
} else {
lockFile.Packages = append(lockFile.Packages, packageData)
}

err = stew.WriteLockFileJSON(lockFile, stewLockFilePath)
stew.CatchAndExit(err)
err = stew.WriteLockFileJSON(lockFile, stewLockFilePath)
if err != nil {
return err
}

fmt.Printf("✨ Successfully installed the %v binary in %v\n", constants.GreenColor(binaryName), constants.GreenColor(stewBinPath))
fmt.Printf("✨ Successfully installed the %v binary in %v\n", constants.GreenColor(binaryName), constants.GreenColor(stewBinPath))
return nil
}

func installFromLockFile(pkgs []stew.PackageData, userOS, userArch string, systemInfo stew.SystemInfo) error {
for _, pkg := range pkgs {
err := installOne(pkg, userOS, userArch, systemInfo, true)
if err != nil {
return err
}
}
return nil
}

func installFromStewfile(pkgs []stew.PackageData, userOS, userArch string, systemInfo stew.SystemInfo) {
for _, pkg := range pkgs {
err := installOne(pkg, userOS, userArch, systemInfo, false)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
}
}
2 changes: 1 addition & 1 deletion cmd/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func upgradeOne(binaryName, userOS, userArch string, lockFile stew.LockFile, sys
}
fmt.Printf("✅ Downloaded %v to %v\n", constants.GreenColor(asset), constants.GreenColor(stewPkgPath))

_, err = stew.InstallBinary(downloadPath, repo, systemInfo, &lockFile, true)
_, err = stew.InstallBinary(downloadPath, repo, systemInfo, &lockFile, true, pkg.Binary)
if err != nil {
if err := os.RemoveAll(downloadPath); err != nil {
return err
Expand Down
35 changes: 14 additions & 21 deletions lib/stewfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,46 +77,39 @@ func RemovePackage(pkgs []PackageData, index int) ([]PackageData, error) {
}

// ReadStewfileContents will read the contents of the Stewfile
func ReadStewfileContents(stewfilePath string) ([]string, error) {
func ReadStewfileContents(stewfilePath string) ([]PackageData, error) {
file, err := os.Open(stewfilePath)
if err != nil {
return []string{}, err
return []PackageData{}, err
}
defer file.Close()

scanner := bufio.NewScanner(file)

var packages []string
var packages []PackageData
for scanner.Scan() {
packages = append(packages, scanner.Text())
packageText := scanner.Text()
pkg, err := ParseCLIInput(packageText)
if err != nil {
return []PackageData{}, err
}
packages = append(packages, pkg)
}

if err := scanner.Err(); err != nil {
return []string{}, err
return []PackageData{}, err
}

return packages, nil
}

func ReadStewLockFileContents(lockFilePath string) ([]string, error) {

// ReadStewLockFileContents will read the contents of the Stewfile.lock.json
func ReadStewLockFileContents(lockFilePath string) ([]PackageData, error) {
lockFile, err := readLockFileJSON(lockFilePath)
if err != nil {
return []string{}, err
}

var packages []string
for _, pkg := range lockFile.Packages {
switch pkg.Source {
case "other":
packages = append(packages, pkg.URL)
case "github":
path := fmt.Sprintf("%s/%s@%s", pkg.Owner, pkg.Repo, pkg.Tag)
packages = append(packages, path)
}
return []PackageData{}, err
}

return packages, nil
return lockFile.Packages, nil
}

// NewLockFile creates a new instance of the LockFile struct
Expand Down
Loading

0 comments on commit ce007fb

Please sign in to comment.