refactor: zerolog -> slog, errors.Wrap -> fmt.Error (#49)

* refactor: zerolog -> slog, errors.Wrap -> fmt.Error

* chore: lint

* fix: correctly handle errors

* fix: use parsed level

* fix: use parsed level, log json to file
This commit is contained in:
Vilsol 2023-12-16 06:19:53 -08:00 committed by GitHub
parent 4195463c60
commit 25f544b8fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 331 additions and 291 deletions

View file

@ -4,8 +4,6 @@ linters-settings:
- .Errorf(
- errors.New(
- errors.Unwrap(
- .Wrap(
- .Wrapf(
- .WithMessage(
- .WithMessagef(
- .WithStack(

29
cli/cache/cache.go vendored
View file

@ -4,14 +4,15 @@ import (
"archive/zip"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/puzpuzpuz/xsync/v3"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
)
@ -52,7 +53,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) {
items, err := os.ReadDir(downloadCache)
if err != nil {
return nil, errors.Wrap(err, "failed reading download cache")
return nil, fmt.Errorf("failed reading download cache: %w", err)
}
for _, item := range items {
@ -65,7 +66,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) {
_, err = addFileToCache(item.Name())
if err != nil {
log.Err(err).Str("file", item.Name()).Msg("failed to add file to cache")
slog.Error("failed to add file to cache", slog.String("file", item.Name()), slog.Any("err", err))
}
}
return loadedCache, nil
@ -74,7 +75,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) {
func addFileToCache(filename string) (*File, error) {
cacheFile, err := readCacheFile(filename)
if err != nil {
return nil, errors.Wrap(err, "failed to read cache file")
return nil, fmt.Errorf("failed to read cache file: %w", err)
}
loadedCache.Compute(cacheFile.ModReference, func(oldValue []File, _ bool) ([]File, bool) {
@ -89,19 +90,19 @@ func readCacheFile(filename string) (*File, error) {
path := filepath.Join(downloadCache, filename)
stat, err := os.Stat(path)
if err != nil {
return nil, errors.Wrap(err, "failed to stat file")
return nil, fmt.Errorf("failed to stat file: %w", err)
}
zipFile, err := os.Open(path)
if err != nil {
return nil, errors.Wrap(err, "failed to open file")
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer zipFile.Close()
size := stat.Size()
reader, err := zip.NewReader(zipFile, size)
if err != nil {
return nil, errors.Wrap(err, "failed to read zip")
return nil, fmt.Errorf("failed to read zip: %w", err)
}
var upluginFile *zip.File
@ -117,23 +118,23 @@ func readCacheFile(filename string) (*File, error) {
upluginReader, err := upluginFile.Open()
if err != nil {
return nil, errors.Wrap(err, "failed to open uplugin file")
return nil, fmt.Errorf("failed to open uplugin file: %w", err)
}
var uplugin UPlugin
data, err := io.ReadAll(upluginReader)
if err != nil {
return nil, errors.Wrap(err, "failed to read uplugin file")
return nil, fmt.Errorf("failed to read uplugin file: %w", err)
}
if err := json.Unmarshal(data, &uplugin); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal uplugin file")
return nil, fmt.Errorf("failed to unmarshal uplugin file: %w", err)
}
modReference := strings.TrimSuffix(upluginFile.Name, ".uplugin")
hash, err := getFileHash(filename)
if err != nil {
return nil, errors.Wrap(err, "failed to get file hash")
return nil, fmt.Errorf("failed to get file hash: %w", err)
}
var iconFile *zip.File
@ -147,13 +148,13 @@ func readCacheFile(filename string) (*File, error) {
if iconFile != nil {
iconReader, err := iconFile.Open()
if err != nil {
return nil, errors.Wrap(err, "failed to open icon file")
return nil, fmt.Errorf("failed to open icon file: %w", err)
}
defer iconReader.Close()
data, err := io.ReadAll(iconReader)
if err != nil {
return nil, errors.Wrap(err, "failed to read icon file")
return nil, fmt.Errorf("failed to read icon file: %w", err)
}
iconData := base64.StdEncoding.EncodeToString(data)
icon = &iconData

26
cli/cache/download.go vendored
View file

@ -1,6 +1,7 @@
package cache
import (
"errors"
"fmt"
"io"
"net/http"
@ -8,7 +9,6 @@ import (
"path/filepath"
"sync"
"github.com/pkg/errors"
"github.com/puzpuzpuz/xsync/v3"
"github.com/spf13/viper"
@ -42,7 +42,7 @@ func DownloadOrCache(cacheKey string, hash string, url string, updates chan<- ut
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
if err := os.MkdirAll(downloadCache, 0o777); err != nil {
if !os.IsExist(err) {
return nil, 0, errors.Wrap(err, "failed creating download cache")
return nil, 0, fmt.Errorf("failed creating download cache: %w", err)
}
}
@ -61,7 +61,7 @@ func DownloadOrCache(cacheKey string, hash string, url string, updates chan<- ut
f, err := os.Open(location)
if err != nil {
return nil, 0, errors.Wrap(err, "failed to open file: "+location)
return nil, 0, fmt.Errorf("failed to open file: %s: %w", location, err)
}
return f, group.size, nil
@ -107,7 +107,7 @@ func DownloadOrCache(cacheKey string, hash string, url string, updates chan<- ut
f, err := os.Open(location)
if err != nil {
return nil, 0, errors.Wrap(err, "failed to open file: "+location)
return nil, 0, fmt.Errorf("failed to open file: %s: %w", location, err)
}
return f, size, nil
@ -121,13 +121,13 @@ func downloadInternal(cacheKey string, location string, hash string, url string,
if hash != "" {
f, err := os.Open(location)
if err != nil {
return 0, errors.Wrap(err, "failed to open file: "+location)
return 0, fmt.Errorf("failed to open file: %s: %w", location, err)
}
defer f.Close()
existingHash, err = utils.SHA256Data(f)
if err != nil {
return 0, errors.Wrap(err, "could not compute hash for file: "+location)
return 0, fmt.Errorf("could not compute hash for file: %s: %w", location, err)
}
}
@ -136,16 +136,16 @@ func downloadInternal(cacheKey string, location string, hash string, url string,
}
if err := os.Remove(location); err != nil {
return 0, errors.Wrap(err, "failed to delete file: "+location)
return 0, fmt.Errorf("failed to delete file: %s: %w", location, err)
}
} else if !os.IsNotExist(err) {
return 0, errors.Wrap(err, "failed to stat file: "+location)
return 0, fmt.Errorf("failed to stat file: %s: %w", location, err)
}
if updates != nil {
headResp, err := http.Head(url)
if err != nil {
return 0, errors.Wrap(err, "failed to head: "+url)
return 0, fmt.Errorf("failed to head: %s: %w", url, err)
}
defer headResp.Body.Close()
updates <- utils.GenericProgress{Total: headResp.ContentLength}
@ -158,13 +158,13 @@ func downloadInternal(cacheKey string, location string, hash string, url string,
out, err := os.Create(location)
if err != nil {
return 0, errors.Wrap(err, "failed creating file at: "+location)
return 0, fmt.Errorf("failed creating file at: %s: %w", location, err)
}
defer out.Close()
resp, err := http.Get(url)
if err != nil {
return 0, errors.Wrap(err, "failed to fetch: "+url)
return 0, fmt.Errorf("failed to fetch: %s: %w", url, err)
}
defer resp.Body.Close()
@ -180,7 +180,7 @@ func downloadInternal(cacheKey string, location string, hash string, url string,
_, err = io.Copy(out, progresser)
if err != nil {
return 0, errors.Wrap(err, "failed writing file to disk")
return 0, fmt.Errorf("failed writing file to disk: %w", err)
}
if updates != nil {
@ -189,7 +189,7 @@ func downloadInternal(cacheKey string, location string, hash string, url string,
_, err = addFileToCache(cacheKey)
if err != nil {
return 0, errors.Wrap(err, "failed to add file to cache")
return 0, fmt.Errorf("failed to add file to cache: %w", err)
}
return resp.ContentLength, nil

View file

@ -2,14 +2,14 @@ package cache
import (
"encoding/json"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"time"
"github.com/pkg/errors"
"github.com/puzpuzpuz/xsync/v3"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/utils"
@ -36,7 +36,7 @@ func getFileHash(file string) (string, error) {
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
stat, err := os.Stat(filepath.Join(downloadCache, file))
if err != nil {
return "", errors.Wrap(err, "failed to stat file")
return "", fmt.Errorf("failed to stat file: %w", err)
}
if stat.Size() != cachedHash.Size || stat.ModTime() != cachedHash.Modified {
return cacheFileHash(file)
@ -48,16 +48,16 @@ func cacheFileHash(file string) (string, error) {
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
stat, err := os.Stat(filepath.Join(downloadCache, file))
if err != nil {
return "", errors.Wrap(err, "failed to stat file")
return "", fmt.Errorf("failed to stat file: %w", err)
}
f, err := os.Open(filepath.Join(downloadCache, file))
if err != nil {
return "", errors.Wrap(err, "failed to open file")
return "", fmt.Errorf("failed to open file: %w", err)
}
defer f.Close()
hash, err := utils.SHA256Data(f)
if err != nil {
return "", errors.Wrap(err, "failed to hash file")
return "", fmt.Errorf("failed to hash file: %w", err)
}
hashCache.Store(file, hashInfo{
Hash: hash,
@ -76,19 +76,19 @@ func loadHashCache() {
}
f, err := os.Open(cacheFile)
if err != nil {
log.Warn().Err(err).Msg("failed to open hash cache, recreating")
slog.Warn("failed to open hash cache, recreating", slog.Any("err", err))
return
}
defer f.Close()
hashCacheJSON, err := io.ReadAll(f)
if err != nil {
log.Warn().Err(err).Msg("failed to read hash cache, recreating")
slog.Warn("failed to read hash cache, recreating", slog.Any("err", err))
return
}
if err := json.Unmarshal(hashCacheJSON, &hashCache); err != nil {
log.Warn().Err(err).Msg("failed to unmarshal hash cache, recreating")
slog.Warn("failed to unmarshal hash cache, recreating", slog.Any("err", err))
return
}
}
@ -102,12 +102,12 @@ func saveHashCache() {
})
hashCacheJSON, err := json.Marshal(plainCache)
if err != nil {
log.Warn().Err(err).Msg("failed to marshal hash cache")
slog.Warn("failed to marshal hash cache", slog.Any("err", err))
return
}
if err := os.WriteFile(cacheFile, hashCacheJSON, 0o755); err != nil {
log.Warn().Err(err).Msg("failed to write hash cache")
slog.Warn("failed to write hash cache", slog.Any("err", err))
return
}
}

View file

@ -1,8 +1,9 @@
package cli
import (
"fmt"
"github.com/Khan/genqlient/graphql"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/cli/cache"
@ -35,17 +36,17 @@ func InitCLI(apiOnly bool) (*GlobalContext, error) {
if !apiOnly {
profiles, err := InitProfiles()
if err != nil {
return nil, errors.Wrap(err, "failed to initialize profiles")
return nil, fmt.Errorf("failed to initialize profiles: %w", err)
}
installations, err := InitInstallations()
if err != nil {
return nil, errors.Wrap(err, "failed to initialize installations")
return nil, fmt.Errorf("failed to initialize installations: %w", err)
}
_, err = cache.LoadCache()
if err != nil {
return nil, errors.Wrap(err, "failed to load cache")
return nil, fmt.Errorf("failed to load cache: %w", err)
}
globalContext = &GlobalContext{
@ -70,12 +71,12 @@ func InitCLI(apiOnly bool) (*GlobalContext, error) {
func (g *GlobalContext) ReInit() error {
profiles, err := InitProfiles()
if err != nil {
return errors.Wrap(err, "failed to initialize profiles")
return fmt.Errorf("failed to initialize profiles: %w", err)
}
installations, err := InitInstallations()
if err != nil {
return errors.Wrap(err, "failed to initialize installations")
return fmt.Errorf("failed to initialize installations: %w", err)
}
g.Installations = installations
@ -89,18 +90,18 @@ func (g *GlobalContext) Wipe() error {
// Wipe all installations
for _, installation := range g.Installations.Installations {
if err := installation.Wipe(); err != nil {
return errors.Wrap(err, "failed wiping installation")
return fmt.Errorf("failed wiping installation: %w", err)
}
if err := g.Installations.DeleteInstallation(installation.Path); err != nil {
return errors.Wrap(err, "failed deleting installation")
return fmt.Errorf("failed deleting installation: %w", err)
}
}
// Wipe all profiles
for _, profile := range g.Profiles.Profiles {
if err := g.Profiles.DeleteProfile(profile.Name); err != nil {
return errors.Wrap(err, "failed deleting profile")
return fmt.Errorf("failed deleting profile: %w", err)
}
}
@ -109,11 +110,11 @@ func (g *GlobalContext) Wipe() error {
func (g *GlobalContext) Save() error {
if err := g.Installations.Save(); err != nil {
return errors.Wrap(err, "failed to save installations")
return fmt.Errorf("failed to save installations: %w", err)
}
if err := g.Profiles.Save(); err != nil {
return errors.Wrap(err, "failed to save profiles")
return fmt.Errorf("failed to save profiles: %w", err)
}
return nil

View file

@ -2,15 +2,15 @@ package disk
import (
"bytes"
"fmt"
"io"
"log/slog"
"net/url"
"strings"
"sync"
"time"
"github.com/jlaffaye/ftp"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
var _ Disk = (*ftpDisk)(nil)
@ -36,20 +36,20 @@ func (f ftpEntry) Name() string {
func newFTP(path string) (Disk, error) {
u, err := url.Parse(path)
if err != nil {
return nil, errors.Wrap(err, "failed to parse ftp url")
return nil, fmt.Errorf("failed to parse ftp url: %w", err)
}
c, err := ftp.Dial(u.Host, ftp.DialWithTimeout(time.Second*5))
if err != nil {
return nil, errors.Wrap(err, "failed to dial host "+u.Host)
return nil, fmt.Errorf("failed to dial host %s: %w", u.Host, err)
}
password, _ := u.User.Password()
if err := c.Login(u.User.Username(), password); err != nil {
return nil, errors.Wrap(err, "failed to login")
return nil, fmt.Errorf("failed to login: %w", err)
}
log.Debug().Msg("logged into ftp")
slog.Debug("logged into ftp")
return &ftpDisk{
path: u.Path,
@ -61,52 +61,64 @@ func (l *ftpDisk) Exists(path string) error {
l.stepLock.Lock()
defer l.stepLock.Unlock()
log.Debug().Str("path", path).Str("schema", "ftp").Msg("checking if file exists")
slog.Debug("checking if file exists", slog.String("path", path), slog.String("schema", "ftp"))
_, err := l.client.FileSize(path)
return errors.Wrap(err, "failed to check if file exists")
return fmt.Errorf("failed to check if file exists: %w", err)
}
func (l *ftpDisk) Read(path string) ([]byte, error) {
l.stepLock.Lock()
defer l.stepLock.Unlock()
log.Debug().Str("path", path).Str("schema", "ftp").Msg("reading file")
slog.Debug("reading file", slog.String("path", path), slog.String("schema", "ftp"))
f, err := l.client.Retr(path)
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve path")
return nil, fmt.Errorf("failed to retrieve path: %w", err)
}
defer f.Close()
data, err := io.ReadAll(f)
return data, errors.Wrap(err, "failed to read file")
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
return data, nil
}
func (l *ftpDisk) Write(path string, data []byte) error {
l.stepLock.Lock()
defer l.stepLock.Unlock()
log.Debug().Str("path", path).Str("schema", "ftp").Msg("writing to file")
return errors.Wrap(l.client.Stor(path, bytes.NewReader(data)), "failed to write file")
slog.Debug("writing to file", slog.String("path", path), slog.String("schema", "ftp"))
if err := l.client.Stor(path, bytes.NewReader(data)); err != nil {
return fmt.Errorf("failed to write file: %w", err)
}
return nil
}
func (l *ftpDisk) Remove(path string) error {
l.stepLock.Lock()
defer l.stepLock.Unlock()
log.Debug().Str("path", path).Str("schema", "ftp").Msg("deleting path")
return errors.Wrap(l.client.Delete(path), "failed to delete path")
slog.Debug("deleting path", slog.String("path", path), slog.String("schema", "ftp"))
if err := l.client.Delete(path); err != nil {
return fmt.Errorf("failed to delete path: %w", err)
}
return nil
}
func (l *ftpDisk) MkDir(path string) error {
l.stepLock.Lock()
defer l.stepLock.Unlock()
log.Debug().Str("schema", "ftp").Msg("going to root directory")
slog.Debug("going to root directory", slog.String("schema", "ftp"))
err := l.client.ChangeDir("/")
if err != nil {
return errors.Wrap(err, "failed to change directory")
return fmt.Errorf("failed to change directory: %w", err)
}
split := strings.Split(path[1:], "/")
@ -125,15 +137,15 @@ func (l *ftpDisk) MkDir(path string) error {
}
if !foundDir {
log.Debug().Str("dir", s).Str("schema", "ftp").Msg("making directory")
slog.Debug("making directory", slog.String("dir", s), slog.String("schema", "ftp"))
if err := l.client.MakeDir(s); err != nil {
return errors.Wrap(err, "failed to make directory")
return fmt.Errorf("failed to make directory: %w", err)
}
}
log.Debug().Str("dir", s).Str("schema", "ftp").Msg("entering directory")
slog.Debug("entering directory", slog.String("dir", s), slog.String("schema", "ftp"))
if err := l.client.ChangeDir(s); err != nil {
return errors.Wrap(err, "failed to enter directory")
return fmt.Errorf("failed to enter directory: %w", err)
}
}
@ -150,11 +162,11 @@ func (l *ftpDisk) ReadDirLock(path string, lock bool) ([]Entry, error) {
defer l.stepLock.Unlock()
}
log.Debug().Str("path", path).Str("schema", "ftp").Msg("reading directory")
slog.Debug("reading directory", slog.String("path", path), slog.String("schema", "ftp"))
dir, err := l.client.List(path)
if err != nil {
return nil, errors.Wrap(err, "failed to list files in directory")
return nil, fmt.Errorf("failed to list files in directory: %w", err)
}
entries := make([]Entry, len(dir))
@ -178,7 +190,7 @@ func (l *ftpDisk) IsExist(err error) bool {
func (l *ftpDisk) Open(path string, _ int) (io.WriteCloser, error) {
reader, writer := io.Pipe()
log.Debug().Str("path", path).Str("schema", "ftp").Msg("opening for writing")
slog.Debug("opening for writing", slog.String("path", path), slog.String("schema", "ftp"))
go func() {
l.stepLock.Lock()
@ -186,9 +198,9 @@ func (l *ftpDisk) Open(path string, _ int) (io.WriteCloser, error) {
err := l.client.Stor(path, reader)
if err != nil {
log.Err(err).Msg("failed to store file")
slog.Error("failed to store file", slog.Any("err", err))
}
log.Debug().Str("path", path).Str("schema", "ftp").Msg("write success")
slog.Debug("write success", slog.String("path", path), slog.String("schema", "ftp"))
}()
return writer, nil

View file

@ -1,11 +1,10 @@
package disk
import (
"fmt"
"io"
"log/slog"
"net/url"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
type Disk interface {
@ -49,18 +48,18 @@ type Entry interface {
func FromPath(path string) (Disk, error) {
parsed, err := url.Parse(path)
if err != nil {
return nil, errors.Wrap(err, "failed to parse path")
return nil, fmt.Errorf("failed to parse path: %w", err)
}
switch parsed.Scheme {
case "ftp":
log.Info().Str("path", path).Msg("connecting to ftp")
slog.Info("connecting to ftp", slog.String("path", path))
return newFTP(path)
case "sftp":
log.Info().Str("path", path).Msg("connecting to sftp")
slog.Info("connecting to sftp", slog.String("path", path))
return newSFTP(path)
}
log.Info().Str("path", path).Msg("using local disk")
slog.Info("using local disk", slog.String("path", path))
return newLocal(path)
}

View file

@ -2,7 +2,9 @@ package cli
import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"net/url"
"os"
"path/filepath"
@ -10,8 +12,6 @@ import (
"strings"
"sync"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/spf13/viper"
"golang.org/x/sync/errgroup"
@ -50,18 +50,18 @@ func InitInstallations() (*Installations, error) {
_, err := os.Stat(installationsFile)
if err != nil {
if !os.IsNotExist(err) {
return nil, errors.Wrap(err, "failed to stat installations file")
return nil, fmt.Errorf("failed to stat installations file: %w", err)
}
_, err := os.Stat(localDir)
if err != nil {
if !os.IsNotExist(err) {
return nil, errors.Wrap(err, "failed to read cache directory")
return nil, fmt.Errorf("failed to read cache directory: %w", err)
}
err = os.MkdirAll(localDir, 0o755)
if err != nil {
return nil, errors.Wrap(err, "failed to create cache directory")
return nil, fmt.Errorf("failed to create cache directory: %w", err)
}
}
@ -70,18 +70,18 @@ func InitInstallations() (*Installations, error) {
}
if err := emptyInstallations.Save(); err != nil {
return nil, errors.Wrap(err, "failed to save empty installations")
return nil, fmt.Errorf("failed to save empty installations: %w", err)
}
}
installationsData, err := os.ReadFile(installationsFile)
if err != nil {
return nil, errors.Wrap(err, "failed to read installations")
return nil, fmt.Errorf("failed to read installations: %w", err)
}
var installations Installations
if err := json.Unmarshal(installationsData, &installations); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal installations")
return nil, fmt.Errorf("failed to unmarshal installations: %w", err)
}
if installations.Version >= nextInstallationsVersion {
@ -93,21 +93,21 @@ func InitInstallations() (*Installations, error) {
func (i *Installations) Save() error {
if viper.GetBool("dry-run") {
log.Info().Msg("dry-run: skipping installation saving")
slog.Info("dry-run: skipping installation saving")
return nil
}
installationsFile := filepath.Join(viper.GetString("local-dir"), viper.GetString("installations-file"))
log.Info().Str("path", installationsFile).Msg("saving installations")
slog.Info("saving installations", slog.String("path", installationsFile))
installationsJSON, err := json.MarshalIndent(i, "", " ")
if err != nil {
return errors.Wrap(err, "failed to marshal installations")
return fmt.Errorf("failed to marshal installations: %w", err)
}
if err := os.WriteFile(installationsFile, installationsJSON, 0o755); err != nil {
return errors.Wrap(err, "failed to write installations")
return fmt.Errorf("failed to write installations: %w", err)
}
return nil
@ -116,7 +116,7 @@ func (i *Installations) Save() error {
func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string, profile string) (*Installation, error) {
parsed, err := url.Parse(installPath)
if err != nil {
return nil, errors.Wrap(err, "failed to parse path")
return nil, fmt.Errorf("failed to parse path: %w", err)
}
absolutePath := installPath
@ -124,7 +124,7 @@ func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string,
absolutePath, err = filepath.Abs(installPath)
if err != nil {
return nil, errors.Wrap(err, "could not resolve absolute path of: "+installPath)
return nil, fmt.Errorf("could not resolve absolute path of: %s: %w", installPath, err)
}
}
@ -135,7 +135,7 @@ func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string,
}
if err := installation.Validate(ctx); err != nil {
return nil, errors.Wrap(err, "failed to validate installation")
return nil, fmt.Errorf("failed to validate installation: %w", err)
}
found := false
@ -206,7 +206,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error {
err = d.Exists(filepath.Join(i.BasePath(), "FactoryGame.exe"))
if err != nil {
if !d.IsNotExist(err) {
return errors.Wrap(err, "failed reading FactoryGame.exe")
return fmt.Errorf("failed reading FactoryGame.exe: %w", err)
}
} else {
foundExecutable = true
@ -215,7 +215,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error {
err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.sh"))
if err != nil {
if !d.IsNotExist(err) {
return errors.Wrap(err, "failed reading FactoryServer.sh")
return fmt.Errorf("failed reading FactoryServer.sh: %w", err)
}
} else {
foundExecutable = true
@ -224,7 +224,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error {
err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.exe"))
if err != nil {
if !d.IsNotExist(err) {
return errors.Wrap(err, "failed reading FactoryServer.exe")
return fmt.Errorf("failed reading FactoryServer.exe: %w", err)
}
} else {
foundExecutable = true
@ -273,11 +273,11 @@ func (i *Installation) LockFile(ctx *GlobalContext) (*resolver.LockFile, error)
lockFileJSON, err := d.Read(lockfilePath)
if err != nil {
if !d.IsNotExist(err) {
return nil, errors.Wrap(err, "failed reading lockfile")
return nil, fmt.Errorf("failed reading lockfile: %w", err)
}
} else {
if err := json.Unmarshal(lockFileJSON, &lockFile); err != nil {
return nil, errors.Wrap(err, "failed parsing lockfile")
return nil, fmt.Errorf("failed parsing lockfile: %w", err)
}
}
@ -298,17 +298,17 @@ func (i *Installation) WriteLockFile(ctx *GlobalContext, lockfile *resolver.Lock
lockfileDir := filepath.Dir(lockfilePath)
if err := d.Exists(lockfileDir); d.IsNotExist(err) {
if err := d.MkDir(lockfileDir); err != nil {
return errors.Wrap(err, "failed creating lockfile directory")
return fmt.Errorf("failed creating lockfile directory: %w", err)
}
}
marshaledLockfile, err := json.MarshalIndent(lockfile, "", " ")
if err != nil {
return errors.Wrap(err, "failed to serialize lockfile json")
return fmt.Errorf("failed to serialize lockfile json: %w", err)
}
if err := d.Write(lockfilePath, marshaledLockfile); err != nil {
return errors.Wrap(err, "failed writing lockfile")
return fmt.Errorf("failed writing lockfile: %w", err)
}
return nil
@ -322,7 +322,7 @@ func (i *Installation) Wipe() error {
modsDirectory := filepath.Join(i.BasePath(), "FactoryGame", "Mods")
if err := d.Remove(modsDirectory); err != nil {
return errors.Wrap(err, "failed removing Mods directory")
return fmt.Errorf("failed removing Mods directory: %w", err)
}
return nil
@ -338,16 +338,16 @@ func (i *Installation) ResolveProfile(ctx *GlobalContext) (*resolver.LockFile, e
gameVersion, err := i.GetGameVersion(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to detect game version")
return nil, fmt.Errorf("failed to detect game version: %w", err)
}
lockfile, err := ctx.Profiles.Profiles[i.Profile].Resolve(depResolver, lockFile, gameVersion)
if err != nil {
return nil, errors.Wrap(err, "could not resolve mods")
return nil, fmt.Errorf("could not resolve mods: %w", err)
}
if err := i.WriteLockFile(ctx, lockfile); err != nil {
return nil, errors.Wrap(err, "failed to write lockfile")
return nil, fmt.Errorf("failed to write lockfile: %w", err)
}
return lockfile, nil
@ -375,12 +375,12 @@ type InstallUpdateItem struct {
func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) error {
if err := i.Validate(ctx); err != nil {
return errors.Wrap(err, "failed to validate installation")
return fmt.Errorf("failed to validate installation: %w", err)
}
platform, err := i.GetPlatform(ctx)
if err != nil {
return errors.Wrap(err, "failed to detect platform")
return fmt.Errorf("failed to detect platform: %w", err)
}
lockfile := resolver.NewLockfile()
@ -389,7 +389,7 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
var err error
lockfile, err = i.ResolveProfile(ctx)
if err != nil {
return errors.Wrap(err, "failed to resolve lockfile")
return fmt.Errorf("failed to resolve lockfile: %w", err)
}
}
@ -400,12 +400,12 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
modsDirectory := filepath.Join(i.BasePath(), "FactoryGame", "Mods")
if err := d.MkDir(modsDirectory); err != nil {
return errors.Wrap(err, "failed creating Mods directory")
return fmt.Errorf("failed creating Mods directory: %w", err)
}
dir, err := d.ReadDir(modsDirectory)
if err != nil {
return errors.Wrap(err, "failed to read mods directory")
return fmt.Errorf("failed to read mods directory: %w", err)
}
for _, entry := range dir {
@ -414,16 +414,16 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
modDir := filepath.Join(modsDirectory, entry.Name())
err := d.Exists(filepath.Join(modDir, ".smm"))
if err == nil {
log.Info().Str("mod_reference", entry.Name()).Msg("deleting mod")
slog.Info("deleting mod", slog.String("mod_reference", entry.Name()))
if err := d.Remove(modDir); err != nil {
return errors.Wrap(err, "failed to delete mod directory")
return fmt.Errorf("failed to delete mod directory: %w", err)
}
}
}
}
}
log.Info().Int("concurrency", viper.GetInt("concurrent-downloads")).Str("path", i.Path).Msg("starting installation")
slog.Info("starting installation", slog.Int("concurrency", viper.GetInt("concurrent-downloads")), slog.String("path", i.Path))
errg := errgroup.Group{}
channelUsers := sync.WaitGroup{}
@ -461,14 +461,14 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
target, ok := version.Targets[platform.TargetName]
if !ok {
return errors.Errorf("%s@%s not available for %s", modReference, version.Version, platform.TargetName)
return fmt.Errorf("%s@%s not available for %s", modReference, version.Version, platform.TargetName)
}
// Only install if a link is provided, otherwise assume mod is already installed
if target.Link != "" {
err := downloadAndExtractMod(modReference, version.Version, target.Link, target.Hash, modsDirectory, updates, downloadSemaphore, d)
if err != nil {
return errors.Wrapf(err, "failed to install %s@%s", modReference, version.Version)
return fmt.Errorf("failed to install %s@%s: %w", modReference, version.Version, err)
}
}
@ -487,7 +487,7 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
}
if err := errg.Wait(); err != nil {
return errors.Wrap(err, "failed to install mods")
return fmt.Errorf("failed to install mods: %w", err)
}
return nil
@ -495,19 +495,19 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
func (i *Installation) UpdateMods(ctx *GlobalContext, mods []string) error {
if err := i.Validate(ctx); err != nil {
return errors.Wrap(err, "failed to validate installation")
return fmt.Errorf("failed to validate installation: %w", err)
}
lockFile, err := i.LockFile(ctx)
if err != nil {
return errors.Wrap(err, "failed to read lock file")
return fmt.Errorf("failed to read lock file: %w", err)
}
resolver := resolver.NewDependencyResolver(ctx.Provider, viper.GetString("api-base"))
gameVersion, err := i.GetGameVersion(ctx)
if err != nil {
return errors.Wrap(err, "failed to detect game version")
return fmt.Errorf("failed to detect game version: %w", err)
}
profile := ctx.Profiles.GetProfile(i.Profile)
@ -521,11 +521,11 @@ func (i *Installation) UpdateMods(ctx *GlobalContext, mods []string) error {
newLockFile, err := profile.Resolve(resolver, lockFile, gameVersion)
if err != nil {
return errors.Wrap(err, "failed to resolve dependencies")
return fmt.Errorf("failed to resolve dependencies: %w", err)
}
if err := i.WriteLockFile(ctx, newLockFile); err != nil {
return errors.Wrap(err, "failed to write lock file")
return fmt.Errorf("failed to write lock file: %w", err)
}
return nil
@ -552,10 +552,10 @@ func downloadAndExtractMod(modReference string, version string, link string, has
}()
}
log.Info().Str("mod_reference", modReference).Str("version", version).Str("link", link).Msg("downloading mod")
slog.Info("downloading mod", slog.String("mod_reference", modReference), slog.String("version", version), slog.String("link", link))
reader, size, err := cache.DownloadOrCache(modReference+"_"+version+".zip", hash, link, downloadUpdates, downloadSemaphore)
if err != nil {
return errors.Wrap(err, "failed to download "+modReference+" from: "+link)
return fmt.Errorf("failed to download %s from: %s: %w", modReference, link, err)
}
defer reader.Close()
@ -583,9 +583,9 @@ func downloadAndExtractMod(modReference string, version string, link string, has
}()
}
log.Info().Str("mod_reference", modReference).Str("version", version).Str("link", link).Msg("extracting mod")
slog.Info("extracting mod", slog.String("mod_reference", modReference), slog.String("version", version), slog.String("link", link))
if err := utils.ExtractMod(reader, size, filepath.Join(modsDirectory, modReference), hash, extractUpdates, d); err != nil {
return errors.Wrap(err, "could not extract "+modReference)
return fmt.Errorf("could not extract %s: %w", modReference, err)
}
if updates != nil {
@ -638,7 +638,7 @@ type gameVersionFile struct {
func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
if err := i.Validate(ctx); err != nil {
return 0, errors.Wrap(err, "failed to validate installation")
return 0, fmt.Errorf("failed to validate installation: %w", err)
}
platform, err := i.GetPlatform(ctx)
@ -655,14 +655,14 @@ func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
file, err := d.Read(fullPath)
if err != nil {
if d.IsNotExist(err) {
return 0, errors.Wrap(err, "could not find game version file")
return 0, fmt.Errorf("could not find game version file: %w", err)
}
return 0, errors.Wrap(err, "failed reading version file")
return 0, fmt.Errorf("failed reading version file: %w", err)
}
var versionData gameVersionFile
if err := json.Unmarshal(file, &versionData); err != nil {
return 0, errors.Wrap(err, "failed to parse version file json")
return 0, fmt.Errorf("failed to parse version file json: %w", err)
}
return versionData.Changelist, nil
@ -670,7 +670,7 @@ func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
func (i *Installation) GetPlatform(ctx *GlobalContext) (*Platform, error) {
if err := i.Validate(ctx); err != nil {
return nil, errors.Wrap(err, "failed to validate installation")
return nil, fmt.Errorf("failed to validate installation: %w", err)
}
d, err := i.GetDisk()
@ -685,7 +685,7 @@ func (i *Installation) GetPlatform(ctx *GlobalContext) (*Platform, error) {
if d.IsNotExist(err) {
continue
}
return nil, errors.Wrap(err, "failed detecting version file")
return nil, fmt.Errorf("failed detecting version file: %w", err)
}
return &platform, nil
}

View file

@ -3,13 +3,13 @@ package cli
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/spf13/viper"
@ -62,18 +62,18 @@ func InitProfiles() (*Profiles, error) {
_, err := os.Stat(profilesFile)
if err != nil {
if !os.IsNotExist(err) {
return nil, errors.Wrap(err, "failed to stat profiles file")
return nil, fmt.Errorf("failed to stat profiles file: %w", err)
}
_, err := os.Stat(localDir)
if err != nil {
if !os.IsNotExist(err) {
return nil, errors.Wrap(err, "failed to read cache directory")
return nil, fmt.Errorf("failed to read cache directory: %w", err)
}
err = os.MkdirAll(localDir, 0o755)
if err != nil {
return nil, errors.Wrap(err, "failed to create cache directory")
return nil, fmt.Errorf("failed to create cache directory: %w", err)
}
}
@ -94,13 +94,13 @@ func InitProfiles() (*Profiles, error) {
if err == nil {
manifestBytes, err := os.ReadFile(manifestFile)
if err != nil {
log.Err(err).Str("file", manifestFile).Msg("Failed to read file, not importing profile")
slog.Error("Failed to read file, not importing profile", slog.Any("err", err), slog.String("file", manifestFile))
continue
}
var smmProfile smmProfileFile
if err := json.Unmarshal(manifestBytes, &smmProfile); err != nil {
log.Err(err).Str("file", manifestFile).Msg("Failed to parse file, not importing profile")
slog.Error("Failed to parse file, not importing profile", slog.Any("err", err), slog.String("file", manifestFile))
continue
}
@ -134,18 +134,18 @@ func InitProfiles() (*Profiles, error) {
}
if err := bootstrapProfiles.Save(); err != nil {
return nil, errors.Wrap(err, "failed to save empty profiles")
return nil, fmt.Errorf("failed to save empty profiles: %w", err)
}
}
profilesData, err := os.ReadFile(profilesFile)
if err != nil {
return nil, errors.Wrap(err, "failed to read profiles")
return nil, fmt.Errorf("failed to read profiles: %w", err)
}
var profiles Profiles
if err := json.Unmarshal(profilesData, &profiles); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal profiles")
return nil, fmt.Errorf("failed to unmarshal profiles: %w", err)
}
if profiles.Version >= nextProfilesVersion {
@ -169,21 +169,21 @@ func InitProfiles() (*Profiles, error) {
// Save the profiles to the profiles file.
func (p *Profiles) Save() error {
if viper.GetBool("dry-run") {
log.Info().Msg("dry-run: skipping profile saving")
slog.Info("dry-run: skipping profile saving")
return nil
}
profilesFile := filepath.Join(viper.GetString("local-dir"), viper.GetString("profiles-file"))
log.Info().Str("path", profilesFile).Msg("saving profiles")
slog.Info("saving profiles", slog.String("path", profilesFile))
profilesJSON, err := json.MarshalIndent(p, "", " ")
if err != nil {
return errors.Wrap(err, "failed to marshal profiles")
return fmt.Errorf("failed to marshal profiles: %w", err)
}
if err := os.WriteFile(profilesFile, profilesJSON, 0o755); err != nil {
return errors.Wrap(err, "failed to write profiles")
return fmt.Errorf("failed to write profiles: %w", err)
}
return nil
@ -301,7 +301,7 @@ func (p *Profile) Resolve(resolver resolver.DependencyResolver, lockFile *resolv
resultLockfile, err := resolver.ResolveModDependencies(context.TODO(), toResolve, lockFile, gameVersion, p.RequiredTargets)
if err != nil {
return nil, errors.Wrap(err, "failed resolving profile dependencies")
return nil, fmt.Errorf("failed resolving profile dependencies: %w", err)
}
return resultLockfile, nil

View file

@ -2,9 +2,9 @@ package provider
import (
"context"
"errors"
"github.com/Khan/genqlient/graphql"
"github.com/pkg/errors"
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/satisfactorymodding/ficsit-cli/ficsit"

View file

@ -2,10 +2,11 @@ package provider
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/pkg/errors"
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/satisfactorymodding/ficsit-cli/cli/cache"
@ -21,7 +22,7 @@ func NewLocalProvider() LocalProvider {
func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit.ModsResponse, error) {
cachedMods, err := cache.GetCache()
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
return nil, fmt.Errorf("failed to get cache: %w", err)
}
mods := make([]ficsit.ModsModsGetModsModsMod, 0)
@ -93,7 +94,7 @@ func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit
func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.GetModResponse, error) {
cachedModFiles, err := cache.GetCacheMod(modReference)
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
return nil, fmt.Errorf("failed to get cache: %w", err)
}
if len(cachedModFiles) == 0 {
@ -129,7 +130,7 @@ func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.G
func (p LocalProvider) SMLVersions(_ context.Context) ([]resolver.SMLVersion, error) {
cachedSMLFiles, err := cache.GetCacheMod("SML")
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
return nil, fmt.Errorf("failed to get cache: %w", err)
}
smlVersions := make([]resolver.SMLVersion, 0)
@ -148,7 +149,7 @@ func (p LocalProvider) SMLVersions(_ context.Context) ([]resolver.SMLVersion, er
func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) {
cachedModFiles, err := cache.GetCacheMod(modID)
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
return nil, fmt.Errorf("failed to get cache: %w", err)
}
versions := make([]resolver.ModVersion, 0)
@ -166,7 +167,7 @@ func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID stri
func (p LocalProvider) GetModName(_ context.Context, modReference string) (*resolver.ModName, error) {
cachedModFiles, err := cache.GetCacheMod(modReference)
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
return nil, fmt.Errorf("failed to get cache: %w", err)
}
if len(cachedModFiles) == 0 {

View file

@ -2,12 +2,12 @@ package cli
import (
"context"
"log/slog"
"math"
"os"
"testing"
"github.com/MarvinJWendt/testza"
"github.com/rs/zerolog/log"
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/spf13/viper"
@ -24,9 +24,9 @@ func installWatcher() chan<- InstallUpdate {
for i := range c {
if i.Progress.Total == i.Progress.Completed {
if i.Type != InstallUpdateTypeOverall {
log.Info().Str("mod_reference", i.Item.Mod).Str("version", i.Item.Version).Str("type", string(i.Type)).Msg("progress completed")
slog.Info("progress completed", slog.String("mod_reference", i.Item.Mod), slog.String("version", i.Item.Version), slog.Any("type", i.Type))
} else {
log.Info().Msg("overall completed")
slog.Info("overall completed")
}
}
}

View file

@ -1,7 +1,8 @@
package cmd
import (
"github.com/rs/zerolog/log"
"log/slog"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -13,10 +14,11 @@ var cliCmd = &cobra.Command{
Use: "cli",
Short: "Start interactive CLI (default)",
RunE: func(cmd *cobra.Command, args []string) error {
log.Info().
Str("version", viper.GetString("version")).
Str("commit", viper.GetString("commit")).
Msg("interactive cli initialized")
slog.Info(
"interactive cli initialized",
slog.String("version", viper.GetString("version")),
slog.String("commit", viper.GetString("commit")),
)
global, err := cli.InitCLI(false)
if err != nil {

View file

@ -1,7 +1,8 @@
package installation
import (
"github.com/pkg/errors"
"errors"
"github.com/spf13/cobra"
"github.com/satisfactorymodding/ficsit-cli/cli"

View file

@ -1,7 +1,8 @@
package installation
import (
"github.com/pkg/errors"
"errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"

View file

@ -1,7 +1,8 @@
package profile
import (
"github.com/pkg/errors"
"errors"
"github.com/spf13/cobra"
"github.com/satisfactorymodding/ficsit-cli/cli"

View file

@ -1,16 +1,16 @@
package cmd
import (
"io"
"fmt"
"log/slog"
"os"
"path/filepath"
"runtime"
"time"
"github.com/pkg/errors"
"github.com/lmittmann/tint"
"github.com/pterm/pterm"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
slogmulti "github.com/samber/slog-multi"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -31,37 +31,56 @@ var RootCmd = &cobra.Command{
_ = viper.ReadInConfig()
level, err := zerolog.ParseLevel(viper.GetString("log"))
if err != nil {
panic(err)
}
zerolog.SetGlobalLevel(level)
writers := make([]io.Writer, 0)
handlers := make([]slog.Handler, 0)
if viper.GetBool("pretty") {
pterm.EnableStyling()
} else {
pterm.DisableStyling()
}
const (
ansiReset = "\033[0m"
ansiBold = "\033[1m"
ansiWhite = "\033[38m"
ansiBrightMagenta = "\033[95m"
)
level := slog.LevelInfo
if err := (&level).UnmarshalText([]byte(viper.GetString("log"))); err != nil {
return fmt.Errorf("failed parsing level: %w", err)
}
if !viper.GetBool("quiet") {
writers = append(writers, zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: time.RFC3339,
})
handlers = append(handlers, tint.NewHandler(os.Stdout, &tint.Options{
Level: level,
AddSource: true,
TimeFormat: time.RFC3339Nano,
ReplaceAttr: func(groups []string, attr slog.Attr) slog.Attr {
if attr.Key == slog.LevelKey {
level := attr.Value.Any().(slog.Level)
if level == slog.LevelDebug {
attr.Value = slog.StringValue(ansiBrightMagenta + "DBG" + ansiReset)
}
} else if attr.Key == slog.MessageKey {
attr.Value = slog.StringValue(ansiBold + ansiWhite + fmt.Sprint(attr.Value.Any()) + ansiReset)
}
return attr
},
}))
}
if viper.GetString("log-file") != "" {
logFile, err := os.OpenFile(viper.GetString("log-file"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o777)
if err != nil {
return errors.Wrap(err, "failed to open log file")
return fmt.Errorf("failed to open log file: %w", err)
}
writers = append(writers, logFile)
handlers = append(handlers, slog.NewJSONHandler(logFile, &slog.HandlerOptions{}))
}
log.Logger = zerolog.New(io.MultiWriter(writers...)).With().Timestamp().Logger()
slog.SetDefault(slog.New(
slogmulti.Fanout(handlers...),
))
return nil
},

View file

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -58,7 +57,7 @@ var searchCmd = &cobra.Command{
case "json":
result, err := json.MarshalIndent(modList, "", " ")
if err != nil {
return errors.Wrap(err, "failed converting mods to json")
return fmt.Errorf("failed converting mods to json: %w", err)
}
println(string(result))
}

View file

@ -3,7 +3,10 @@ package smr
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"math"
"mime/multipart"
"net/http"
@ -12,8 +15,6 @@ import (
"strings"
"time"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -57,7 +58,7 @@ var uploadCmd = &cobra.Command{
stat, err := os.Stat(filePath)
if err != nil {
return errors.Wrap(err, "failed to stat file")
return fmt.Errorf("failed to stat file: %w", err)
}
global, err := cli.InitCLI(true)
@ -71,31 +72,31 @@ var uploadCmd = &cobra.Command{
// TODO Validate .smod file before upload
logBase := log.With().Str("mod-id", modID).Str("path", filePath).Logger()
logBase.Info().Msg("creating a new mod version")
logBase := slog.With(slog.String("mod-id", modID), slog.String("path", filePath))
logBase.Info("creating a new mod version")
createdVersion, err := ficsit.CreateVersion(cmd.Context(), global.APIClient, modID)
if err != nil {
return err
}
logBase = logBase.With().Str("version-id", createdVersion.GetVersionID()).Logger()
logBase.Info().Msg("received version id")
logBase = logBase.With(slog.String("version-id", createdVersion.GetVersionID()))
logBase.Info("received version id")
// TODO Parallelize chunk uploading
chunkCount := int(math.Ceil(float64(stat.Size()) / float64(chunkSize)))
for i := 0; i < chunkCount; i++ {
chunkLog := logBase.With().Int("chunk", i).Logger()
chunkLog.Info().Msg("uploading chunk")
chunkLog := logBase.With(slog.Int("chunk", i))
chunkLog.Info("uploading chunk")
f, err := os.Open(filePath)
if err != nil {
return errors.Wrap(err, "failed to open file")
return fmt.Errorf("failed to open file: %w", err)
}
offset := int64(i) * chunkSize
if _, err := f.Seek(offset, 0); err != nil {
return errors.Wrap(err, "failed to seek to chunk offset")
return fmt.Errorf("failed to seek to chunk offset: %w", err)
}
bufferSize := chunkSize
@ -105,7 +106,7 @@ var uploadCmd = &cobra.Command{
chunk := make([]byte, bufferSize)
if _, err := f.Read(chunk); err != nil {
return errors.Wrap(err, "failed to read from chunk offset")
return fmt.Errorf("failed to read from chunk offset: %w", err)
}
operationBody, err := json.Marshal(map[string]interface{}{
@ -118,14 +119,14 @@ var uploadCmd = &cobra.Command{
},
})
if err != nil {
return errors.Wrap(err, "failed to serialize operation body")
return fmt.Errorf("failed to serialize operation body: %w", err)
}
mapBody, err := json.Marshal(map[string]interface{}{
"0": []string{"variables.file"},
})
if err != nil {
return errors.Wrap(err, "failed to serialize map body")
return fmt.Errorf("failed to serialize map body: %w", err)
}
body := &bytes.Buffer{}
@ -133,33 +134,33 @@ var uploadCmd = &cobra.Command{
operations, err := writer.CreateFormField("operations")
if err != nil {
return errors.Wrap(err, "failed to create operations field")
return fmt.Errorf("failed to create operations field: %w", err)
}
if _, err := operations.Write(operationBody); err != nil {
return errors.Wrap(err, "failed to write to operation field")
return fmt.Errorf("failed to write to operation field: %w", err)
}
mapField, err := writer.CreateFormField("map")
if err != nil {
return errors.Wrap(err, "failed to create map field")
return fmt.Errorf("failed to create map field: %w", err)
}
if _, err := mapField.Write(mapBody); err != nil {
return errors.Wrap(err, "failed to write to map field")
return fmt.Errorf("failed to write to map field: %w", err)
}
part, err := writer.CreateFormFile("0", filepath.Base(filePath))
if err != nil {
return errors.Wrap(err, "failed to create file field")
return fmt.Errorf("failed to create file field: %w", err)
}
if _, err := io.Copy(part, bytes.NewReader(chunk)); err != nil {
return errors.Wrap(err, "failed to write to file field")
return fmt.Errorf("failed to write to file field: %w", err)
}
if err := writer.Close(); err != nil {
return errors.Wrap(err, "failed to close body writer")
return fmt.Errorf("failed to close body writer: %w", err)
}
r, _ := http.NewRequest("POST", viper.GetString("api-base")+viper.GetString("graphql-api"), body)
@ -168,12 +169,12 @@ var uploadCmd = &cobra.Command{
client := &http.Client{}
if _, err := client.Do(r); err != nil {
return errors.Wrap(err, "failed to execute request")
return fmt.Errorf("failed to execute request: %w", err)
}
}
logBase.Info().Msg("finalizing uploaded version")
logBase.Info("finalizing uploaded version")
finalizeSuccess, err := ficsit.FinalizeCreateVersion(cmd.Context(), global.APIClient, modID, createdVersion.GetVersionID(), ficsit.NewVersion{
Changelog: changelog,
@ -184,16 +185,16 @@ var uploadCmd = &cobra.Command{
}
if !finalizeSuccess.GetSuccess() {
logBase.Error().Msg("failed to finalize version upload")
logBase.Error("failed to finalize version upload")
}
time.Sleep(time.Second * 1)
for {
logBase.Info().Msg("checking version upload state")
logBase.Info("checking version upload state")
state, err := ficsit.CheckVersionUploadState(cmd.Context(), global.APIClient, modID, createdVersion.GetVersionID())
if err != nil {
logBase.Err(err).Msg("failed to upload mod")
logBase.Error("failed to upload mod", slog.Any("err", err))
return nil
}
@ -203,11 +204,11 @@ var uploadCmd = &cobra.Command{
}
if state.GetState().Auto_approved {
logBase.Info().Msg("version successfully uploaded and auto-approved")
logBase.Info("version successfully uploaded and auto-approved")
break
}
logBase.Info().Msg("version successfully uploaded, but has to be scanned for viruses, which may take up to 15 minutes")
logBase.Info("version successfully uploaded, but has to be scanned for viruses, which may take up to 15 minutes")
break
}

View file

@ -1,10 +1,10 @@
package ficsit
import (
"fmt"
"net/http"
"github.com/Khan/genqlient/graphql"
"github.com/pkg/errors"
"github.com/spf13/viper"
)
@ -19,7 +19,11 @@ func (t *AuthedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
}
rt, err := t.Wrapped.RoundTrip(req)
return rt, errors.Wrap(err, "failed roundtrip")
if err != nil {
return nil, fmt.Errorf("failed roundtrip: %w", err)
}
return rt, nil
}
func InitAPI() graphql.Client {

View file

@ -2,9 +2,8 @@ package utils
import (
"bytes"
"fmt"
"time"
"github.com/pkg/errors"
)
//goland:noinspection GoUnusedExportedFunction
@ -18,7 +17,7 @@ func UnmarshalDateTime(b []byte, v *time.Time) error {
parsed, err := time.Parse(time.RFC3339, string(trimmed))
if err != nil {
return errors.Wrap(err, "failed to parse date time")
return fmt.Errorf("failed to parse date time: %w", err)
}
*v = parsed

19
go.mod
View file

@ -9,21 +9,21 @@ require (
github.com/Khan/genqlient v0.6.0
github.com/MarvinJWendt/testza v0.5.2
github.com/PuerkitoBio/goquery v1.8.1
github.com/charmbracelet/bubbles v0.16.1
github.com/charmbracelet/bubbletea v0.24.2
github.com/charmbracelet/bubbles v0.17.1
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/glamour v0.6.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/charmbracelet/x/exp/teatest v0.0.0-20231206171822-6e7b9b308fe7
github.com/charmbracelet/x/exp/teatest v0.0.0-20231215171016-7ba2b450712d
github.com/jlaffaye/ftp v0.2.0
github.com/lmittmann/tint v1.0.3
github.com/muesli/reflow v0.3.0
github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.71
github.com/puzpuzpuz/xsync/v3 v3.0.2
github.com/rs/zerolog v1.31.0
github.com/sahilm/fuzzy v0.1.0
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f
github.com/samber/slog-multi v1.0.2
github.com/satisfactorymodding/ficsit-resolver v0.0.2
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.0
github.com/spf13/viper v1.18.1
golang.org/x/sync v0.5.0
)
@ -39,7 +39,7 @@ require (
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymanbagabas/go-udiff v0.1.0 // indirect
github.com/aymanbagabas/go-udiff v0.2.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
@ -57,7 +57,6 @@ require (
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
@ -69,10 +68,12 @@ require (
github.com/muesli/termenv v0.15.2 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect

39
go.sum
View file

@ -43,28 +43,27 @@ github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.1.0 h1:9Dpklm2oBBhMxIFbMffmPvDaF7vOYfv9B5HXVr42KMU=
github.com/aymanbagabas/go-udiff v0.1.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs=
github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY=
github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY=
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4=
github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o=
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/charmbracelet/x/exp/teatest v0.0.0-20231206171822-6e7b9b308fe7 h1:lS677S/t1CUy7eq96Z2ZRt0Aqwgq+tshojIBcqDzrtw=
github.com/charmbracelet/x/exp/teatest v0.0.0-20231206171822-6e7b9b308fe7/go.mod h1:TckAxPtan3aJ5wbTgBkySpc50SZhXJRZ8PtYICnZJEw=
github.com/charmbracelet/x/exp/teatest v0.0.0-20231215171016-7ba2b450712d h1:J6mdY8xl7YVGMSbPlqDcg64/J3m7wPuX1OWzPMWW4OA=
github.com/charmbracelet/x/exp/teatest v0.0.0-20231215171016-7ba2b450712d/go.mod h1:43J0pdacLjJQtomu7vU6RFZX3bn84toqNw7hjX8bhmM=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -80,7 +79,6 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
@ -117,14 +115,13 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/lmittmann/tint v1.0.3 h1:W5PHeA2D8bBJVvabNfQD/XW9HPLZK1XoPZH0cq8NouQ=
github.com/lmittmann/tint v1.0.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
@ -178,17 +175,18 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/samber/slog-multi v1.0.2 h1:6BVH9uHGAsiGkbbtQgAOQJMpKgV8unMrHhhJaw+X1EQ=
github.com/samber/slog-multi v1.0.2/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo=
github.com/satisfactorymodding/ficsit-resolver v0.0.2 h1:dj/OsDLpaMUqCHpfBVHvDMUv2nf5gT4HS2ydBMkmtcQ=
github.com/satisfactorymodding/ficsit-resolver v0.0.2/go.mod h1:ckKMmMvDoYbbkEbWXEsMes608uvv6EKphXPhHX8LKSc=
github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
@ -207,8 +205,8 @@ github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.0 h1:pN6W1ub/G4OfnM+NR9p7xP9R6TltLUzp5JG9yZD3Qg0=
github.com/spf13/viper v1.18.0/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spf13/viper v1.18.1 h1:rmuU42rScKWlhhJDyXZRKJQHXFX02chSVW1IvkPGiVM=
github.com/spf13/viper v1.18.1/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -282,7 +280,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

View file

@ -1,10 +1,11 @@
package tea
import (
"fmt"
"github.com/Khan/genqlient/graphql"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/pkg/errors"
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/spf13/viper"
@ -45,7 +46,7 @@ func (m *rootModel) SetCurrentProfile(profile *cli.Profile) error {
if m.GetCurrentInstallation() != nil {
if err := m.GetCurrentInstallation().SetProfile(m.global, profile.Name); err != nil {
return errors.Wrap(err, "failed setting profile on installation")
return fmt.Errorf("failed setting profile on installation: %w", err)
}
}
@ -92,7 +93,7 @@ func (m *rootModel) GetGlobal() *cli.GlobalContext {
func RunTea(global *cli.GlobalContext) error {
if _, err := tea.NewProgram(scenes.NewMainMenu(newModel(global)), tea.WithAltScreen(), tea.WithMouseCellMotion()).Run(); err != nil {
return errors.Wrap(err, "internal tea error")
return fmt.Errorf("internal tea error: %w", err)
}
return nil
}

View file

@ -27,11 +27,11 @@ var _ tea.Model = (*newInstallation)(nil)
type newInstallation struct {
dirList list.Model
input textinput.Model
root components.RootModel
parent tea.Model
error *components.ErrorComponent
title string
input textinput.Model
}
func NewNewInstallation(root components.RootModel, parent tea.Model) tea.Model {

View file

@ -1,6 +1,7 @@
package scenes
import (
"log/slog"
"strings"
"time"
@ -8,7 +9,6 @@ import (
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/tea/components"
@ -114,7 +114,7 @@ func NewMainMenu(root components.RootModel) tea.Model {
ItemTitle: "Apply Changes",
Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) {
if err := root.GetGlobal().Save(); err != nil {
log.Error().Err(err).Msg(errors.ErrorFailedAddMod)
slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err))
errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5)
currentModel.error = errorComponent
return currentModel, cmd
@ -128,7 +128,7 @@ func NewMainMenu(root components.RootModel) tea.Model {
ItemTitle: "Save",
Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) {
if err := root.GetGlobal().Save(); err != nil {
log.Error().Err(err).Msg(errors.ErrorFailedAddMod)
slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err))
errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5)
currentModel.error = errorComponent
return currentModel, cmd

View file

@ -1,13 +1,13 @@
package mods
import (
"log/slog"
"time"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/rs/zerolog/log"
"github.com/satisfactorymodding/ficsit-cli/tea/components"
"github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors"
@ -72,7 +72,7 @@ func NewModMenu(root components.RootModel, parent tea.Model, mod utils.Mod) tea.
Activate: func(msg tea.Msg, currentModel modMenu) (tea.Model, tea.Cmd) {
err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0")
if err != nil {
log.Error().Err(err).Msg(errors.ErrorFailedAddMod)
slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err))
cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod)
return currentModel, cmd
}

View file

@ -2,6 +2,7 @@ package mods
import (
"context"
"log/slog"
"strconv"
"strings"
"time"
@ -15,7 +16,6 @@ import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
"github.com/rs/zerolog/log"
"github.com/satisfactorymodding/ficsit-cli/ficsit"
"github.com/satisfactorymodding/ficsit-cli/tea/components"
@ -192,13 +192,13 @@ func (m modInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
markdownDescription, err := converter.ConvertString(mod.Full_description)
if err != nil {
log.Error().Err(err).Msg("failed to convert html to markdown")
slog.Error("failed to convert html to markdown", slog.Any("err", err))
markdownDescription = mod.Full_description
}
description, err := glamour.Render(markdownDescription, "dark")
if err != nil {
log.Error().Err(err).Msg("failed to render markdown")
slog.Error("failed to render markdown", slog.Any("err", err))
description = mod.Full_description
}

View file

@ -15,12 +15,12 @@ import (
var _ tea.Model = (*modSemver)(nil)
type modSemver struct {
input textinput.Model
root components.RootModel
parent tea.Model
error *components.ErrorComponent
mod utils.Mod
title string
input textinput.Model
}
func NewModSemver(root components.RootModel, parent tea.Model, mod utils.Mod) tea.Model {

View file

@ -1,13 +1,13 @@
package mods
import (
"log/slog"
"time"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/rs/zerolog/log"
"github.com/satisfactorymodding/ficsit-cli/tea/components"
"github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors"
@ -53,7 +53,7 @@ func NewModVersion(root components.RootModel, parent tea.Model, mod utils.Mod) t
Activate: func(msg tea.Msg, currentModel modVersionMenu) (tea.Model, tea.Cmd) {
err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0")
if err != nil {
log.Error().Err(err).Msg(errors.ErrorFailedAddMod)
slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err))
cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod)
return currentModel, cmd
}

View file

@ -33,13 +33,13 @@ func (k keyMap) FullHelp() [][]key.Binding {
}
type newProfile struct {
input textinput.Model
root components.RootModel
parent tea.Model
error *components.ErrorComponent
title string
help help.Model
title string
keys keyMap
input textinput.Model
}
func NewNewProfile(root components.RootModel, parent tea.Model) tea.Model {

View file

@ -17,12 +17,12 @@ import (
var _ tea.Model = (*renameProfile)(nil)
type renameProfile struct {
input textinput.Model
root components.RootModel
parent tea.Model
error *components.ErrorComponent
title string
oldName string
input textinput.Model
}
func NewRenameProfile(root components.RootModel, parent tea.Model, profileData *cli.Profile) tea.Model {

View file

@ -1,6 +1,7 @@
package tea
import (
"errors"
"io"
"os"
"runtime"
@ -11,7 +12,6 @@ import (
"github.com/MarvinJWendt/testza"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/x/exp/teatest"
"github.com/pkg/errors"
"github.com/satisfactorymodding/ficsit-cli/cfg"
"github.com/satisfactorymodding/ficsit-cli/cli"

View file

@ -4,14 +4,13 @@ import (
"archive/zip"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"sync"
"sync/atomic"
"github.com/pkg/errors"
"github.com/satisfactorymodding/ficsit-cli/cli/disk"
)
@ -51,13 +50,17 @@ func (pt *Progresser) Read(p []byte) (int, error) {
return n, io.EOF
}
return n, errors.Wrap(err, "failed to read")
if err != nil {
return 0, fmt.Errorf("failed to read: %w", err)
}
return n, nil
}
func SHA256Data(f io.Reader) (string, error) {
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", errors.Wrap(err, "failed to compute hash")
return "", fmt.Errorf("failed to compute hash: %w", err)
}
return hex.EncodeToString(h.Sum(nil)), nil
@ -68,7 +71,7 @@ func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates
hashBytes, err := d.Read(hashFile)
if err != nil {
if !d.IsNotExist(err) {
return errors.Wrap(err, "failed to read .smm mod hash file")
return fmt.Errorf("failed to read .smm mod hash file: %w", err)
}
} else {
if hash == string(hashBytes) {
@ -78,21 +81,21 @@ func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates
if err := d.MkDir(location); err != nil {
if !d.IsExist(err) {
return errors.Wrap(err, "failed to create mod directory: "+location)
return fmt.Errorf("failed to create mod directory: %s: %w", location, err)
}
if err := d.Remove(location); err != nil {
return errors.Wrap(err, "failed to remove directory: "+location)
return fmt.Errorf("failed to remove directory: %s: %w", location, err)
}
if err := d.MkDir(location); err != nil {
return errors.Wrap(err, "failed to create mod directory: "+location)
return fmt.Errorf("failed to create mod directory: %s: %w", location, err)
}
}
reader, err := zip.NewReader(f, size)
if err != nil {
return errors.Wrap(err, "failed to read file as zip")
return fmt.Errorf("failed to read file as zip: %w", err)
}
totalSize := int64(0)
@ -117,7 +120,7 @@ func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates
outFileLocation := filepath.Join(location, file.Name)
if err := d.MkDir(filepath.Dir(outFileLocation)); err != nil {
return errors.Wrap(err, "failed to create mod directory: "+location)
return fmt.Errorf("failed to create mod directory: %s: %w", location, err)
}
var fileUpdates chan GenericProgress
@ -144,7 +147,7 @@ func ExtractMod(f io.ReaderAt, size int64, location string, hash string, updates
}
if err := d.Write(hashFile, []byte(hash)); err != nil {
return errors.Wrap(err, "failed to write .smm mod hash file")
return fmt.Errorf("failed to write .smm mod hash file: %w", err)
}
if updates != nil {
@ -161,14 +164,14 @@ func writeZipFile(outFileLocation string, file *zip.File, d disk.Disk, updates c
outFile, err := d.Open(outFileLocation, os.O_CREATE|os.O_RDWR)
if err != nil {
return errors.Wrap(err, "failed to write to file: "+outFileLocation)
return fmt.Errorf("failed to write to file: %s: %w", outFileLocation, err)
}
defer outFile.Close()
inFile, err := file.Open()
if err != nil {
return errors.Wrap(err, "failed to process mod zip")
return fmt.Errorf("failed to process mod zip: %w", err)
}
defer inFile.Close()
@ -179,7 +182,7 @@ func writeZipFile(outFileLocation string, file *zip.File, d disk.Disk, updates c
}
if _, err := io.Copy(outFile, progressInReader); err != nil {
return errors.Wrap(err, "failed to write to file: "+outFileLocation)
return fmt.Errorf("failed to write to file: %s: %w", outFileLocation, err)
}
return nil

View file

@ -2,19 +2,18 @@ package utils
import (
"encoding/json"
"github.com/pkg/errors"
"fmt"
)
func Copy[T any](obj T) (*T, error) {
marshal, err := json.Marshal(obj)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal object")
return nil, fmt.Errorf("failed to marshal object: %w", err)
}
out := new(T)
if err := json.Unmarshal(marshal, out); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal object")
return nil, fmt.Errorf("failed to unmarshal object: %w", err)
}
return out, nil