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:
parent
4195463c60
commit
25f544b8fe
35 changed files with 331 additions and 291 deletions
|
@ -4,8 +4,6 @@ linters-settings:
|
||||||
- .Errorf(
|
- .Errorf(
|
||||||
- errors.New(
|
- errors.New(
|
||||||
- errors.Unwrap(
|
- errors.Unwrap(
|
||||||
- .Wrap(
|
|
||||||
- .Wrapf(
|
|
||||||
- .WithMessage(
|
- .WithMessage(
|
||||||
- .WithMessagef(
|
- .WithMessagef(
|
||||||
- .WithStack(
|
- .WithStack(
|
||||||
|
|
29
cli/cache/cache.go
vendored
29
cli/cache/cache.go
vendored
|
@ -4,14 +4,15 @@ import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"github.com/puzpuzpuz/xsync/v3"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) {
|
||||||
|
|
||||||
items, err := os.ReadDir(downloadCache)
|
items, err := os.ReadDir(downloadCache)
|
||||||
if err != nil {
|
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 {
|
for _, item := range items {
|
||||||
|
@ -65,7 +66,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) {
|
||||||
|
|
||||||
_, err = addFileToCache(item.Name())
|
_, err = addFileToCache(item.Name())
|
||||||
if err != nil {
|
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
|
return loadedCache, nil
|
||||||
|
@ -74,7 +75,7 @@ func LoadCache() (*xsync.MapOf[string, []File], error) {
|
||||||
func addFileToCache(filename string) (*File, error) {
|
func addFileToCache(filename string) (*File, error) {
|
||||||
cacheFile, err := readCacheFile(filename)
|
cacheFile, err := readCacheFile(filename)
|
||||||
if err != nil {
|
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) {
|
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)
|
path := filepath.Join(downloadCache, filename)
|
||||||
stat, err := os.Stat(path)
|
stat, err := os.Stat(path)
|
||||||
if err != nil {
|
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)
|
zipFile, err := os.Open(path)
|
||||||
if err != nil {
|
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()
|
defer zipFile.Close()
|
||||||
|
|
||||||
size := stat.Size()
|
size := stat.Size()
|
||||||
reader, err := zip.NewReader(zipFile, size)
|
reader, err := zip.NewReader(zipFile, size)
|
||||||
if err != nil {
|
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
|
var upluginFile *zip.File
|
||||||
|
@ -117,23 +118,23 @@ func readCacheFile(filename string) (*File, error) {
|
||||||
|
|
||||||
upluginReader, err := upluginFile.Open()
|
upluginReader, err := upluginFile.Open()
|
||||||
if err != nil {
|
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
|
var uplugin UPlugin
|
||||||
data, err := io.ReadAll(upluginReader)
|
data, err := io.ReadAll(upluginReader)
|
||||||
if err != nil {
|
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 {
|
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")
|
modReference := strings.TrimSuffix(upluginFile.Name, ".uplugin")
|
||||||
|
|
||||||
hash, err := getFileHash(filename)
|
hash, err := getFileHash(filename)
|
||||||
if err != nil {
|
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
|
var iconFile *zip.File
|
||||||
|
@ -147,13 +148,13 @@ func readCacheFile(filename string) (*File, error) {
|
||||||
if iconFile != nil {
|
if iconFile != nil {
|
||||||
iconReader, err := iconFile.Open()
|
iconReader, err := iconFile.Open()
|
||||||
if err != nil {
|
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()
|
defer iconReader.Close()
|
||||||
|
|
||||||
data, err := io.ReadAll(iconReader)
|
data, err := io.ReadAll(iconReader)
|
||||||
if err != nil {
|
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)
|
iconData := base64.StdEncoding.EncodeToString(data)
|
||||||
icon = &iconData
|
icon = &iconData
|
||||||
|
|
26
cli/cache/download.go
vendored
26
cli/cache/download.go
vendored
|
@ -1,6 +1,7 @@
|
||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -8,7 +9,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"github.com/puzpuzpuz/xsync/v3"
|
||||||
"github.com/spf13/viper"
|
"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")
|
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
|
||||||
if err := os.MkdirAll(downloadCache, 0o777); err != nil {
|
if err := os.MkdirAll(downloadCache, 0o777); err != nil {
|
||||||
if !os.IsExist(err) {
|
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)
|
f, err := os.Open(location)
|
||||||
if err != nil {
|
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
|
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)
|
f, err := os.Open(location)
|
||||||
if err != nil {
|
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
|
return f, size, nil
|
||||||
|
@ -121,13 +121,13 @@ func downloadInternal(cacheKey string, location string, hash string, url string,
|
||||||
if hash != "" {
|
if hash != "" {
|
||||||
f, err := os.Open(location)
|
f, err := os.Open(location)
|
||||||
if err != nil {
|
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()
|
defer f.Close()
|
||||||
|
|
||||||
existingHash, err = utils.SHA256Data(f)
|
existingHash, err = utils.SHA256Data(f)
|
||||||
if err != nil {
|
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 {
|
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) {
|
} 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 {
|
if updates != nil {
|
||||||
headResp, err := http.Head(url)
|
headResp, err := http.Head(url)
|
||||||
if err != nil {
|
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()
|
defer headResp.Body.Close()
|
||||||
updates <- utils.GenericProgress{Total: headResp.ContentLength}
|
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)
|
out, err := os.Create(location)
|
||||||
if err != nil {
|
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()
|
defer out.Close()
|
||||||
|
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
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()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ func downloadInternal(cacheKey string, location string, hash string, url string,
|
||||||
|
|
||||||
_, err = io.Copy(out, progresser)
|
_, err = io.Copy(out, progresser)
|
||||||
if err != nil {
|
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 {
|
if updates != nil {
|
||||||
|
@ -189,7 +189,7 @@ func downloadInternal(cacheKey string, location string, hash string, url string,
|
||||||
|
|
||||||
_, err = addFileToCache(cacheKey)
|
_, err = addFileToCache(cacheKey)
|
||||||
if err != nil {
|
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
|
return resp.ContentLength, nil
|
||||||
|
|
22
cli/cache/integrity.go
vendored
22
cli/cache/integrity.go
vendored
|
@ -2,14 +2,14 @@ package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"github.com/puzpuzpuz/xsync/v3"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/utils"
|
"github.com/satisfactorymodding/ficsit-cli/utils"
|
||||||
|
@ -36,7 +36,7 @@ func getFileHash(file string) (string, error) {
|
||||||
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
|
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
|
||||||
stat, err := os.Stat(filepath.Join(downloadCache, file))
|
stat, err := os.Stat(filepath.Join(downloadCache, file))
|
||||||
if err != nil {
|
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 {
|
if stat.Size() != cachedHash.Size || stat.ModTime() != cachedHash.Modified {
|
||||||
return cacheFileHash(file)
|
return cacheFileHash(file)
|
||||||
|
@ -48,16 +48,16 @@ func cacheFileHash(file string) (string, error) {
|
||||||
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
|
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
|
||||||
stat, err := os.Stat(filepath.Join(downloadCache, file))
|
stat, err := os.Stat(filepath.Join(downloadCache, file))
|
||||||
if err != nil {
|
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))
|
f, err := os.Open(filepath.Join(downloadCache, file))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "failed to open file")
|
return "", fmt.Errorf("failed to open file: %w", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
hash, err := utils.SHA256Data(f)
|
hash, err := utils.SHA256Data(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "failed to hash file")
|
return "", fmt.Errorf("failed to hash file: %w", err)
|
||||||
}
|
}
|
||||||
hashCache.Store(file, hashInfo{
|
hashCache.Store(file, hashInfo{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
|
@ -76,19 +76,19 @@ func loadHashCache() {
|
||||||
}
|
}
|
||||||
f, err := os.Open(cacheFile)
|
f, err := os.Open(cacheFile)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
hashCacheJSON, err := io.ReadAll(f)
|
hashCacheJSON, err := io.ReadAll(f)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(hashCacheJSON, &hashCache); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,12 +102,12 @@ func saveHashCache() {
|
||||||
})
|
})
|
||||||
hashCacheJSON, err := json.Marshal(plainCache)
|
hashCacheJSON, err := json.Marshal(plainCache)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(cacheFile, hashCacheJSON, 0o755); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/Khan/genqlient/graphql"
|
"github.com/Khan/genqlient/graphql"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/cli/cache"
|
"github.com/satisfactorymodding/ficsit-cli/cli/cache"
|
||||||
|
@ -35,17 +36,17 @@ func InitCLI(apiOnly bool) (*GlobalContext, error) {
|
||||||
if !apiOnly {
|
if !apiOnly {
|
||||||
profiles, err := InitProfiles()
|
profiles, err := InitProfiles()
|
||||||
if err != nil {
|
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()
|
installations, err := InitInstallations()
|
||||||
if err != nil {
|
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()
|
_, err = cache.LoadCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to load cache")
|
return nil, fmt.Errorf("failed to load cache: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
globalContext = &GlobalContext{
|
globalContext = &GlobalContext{
|
||||||
|
@ -70,12 +71,12 @@ func InitCLI(apiOnly bool) (*GlobalContext, error) {
|
||||||
func (g *GlobalContext) ReInit() error {
|
func (g *GlobalContext) ReInit() error {
|
||||||
profiles, err := InitProfiles()
|
profiles, err := InitProfiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to initialize profiles")
|
return fmt.Errorf("failed to initialize profiles: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
installations, err := InitInstallations()
|
installations, err := InitInstallations()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to initialize installations")
|
return fmt.Errorf("failed to initialize installations: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Installations = installations
|
g.Installations = installations
|
||||||
|
@ -89,18 +90,18 @@ func (g *GlobalContext) Wipe() error {
|
||||||
// Wipe all installations
|
// Wipe all installations
|
||||||
for _, installation := range g.Installations.Installations {
|
for _, installation := range g.Installations.Installations {
|
||||||
if err := installation.Wipe(); err != nil {
|
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 {
|
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
|
// Wipe all profiles
|
||||||
for _, profile := range g.Profiles.Profiles {
|
for _, profile := range g.Profiles.Profiles {
|
||||||
if err := g.Profiles.DeleteProfile(profile.Name); err != nil {
|
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 {
|
func (g *GlobalContext) Save() error {
|
||||||
if err := g.Installations.Save(); err != nil {
|
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 {
|
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
|
return nil
|
||||||
|
|
|
@ -2,15 +2,15 @@ package disk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jlaffaye/ftp"
|
"github.com/jlaffaye/ftp"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Disk = (*ftpDisk)(nil)
|
var _ Disk = (*ftpDisk)(nil)
|
||||||
|
@ -36,20 +36,20 @@ func (f ftpEntry) Name() string {
|
||||||
func newFTP(path string) (Disk, error) {
|
func newFTP(path string) (Disk, error) {
|
||||||
u, err := url.Parse(path)
|
u, err := url.Parse(path)
|
||||||
if err != nil {
|
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))
|
c, err := ftp.Dial(u.Host, ftp.DialWithTimeout(time.Second*5))
|
||||||
if err != nil {
|
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()
|
password, _ := u.User.Password()
|
||||||
if err := c.Login(u.User.Username(), password); err != nil {
|
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{
|
return &ftpDisk{
|
||||||
path: u.Path,
|
path: u.Path,
|
||||||
|
@ -61,52 +61,64 @@ func (l *ftpDisk) Exists(path string) error {
|
||||||
l.stepLock.Lock()
|
l.stepLock.Lock()
|
||||||
defer l.stepLock.Unlock()
|
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)
|
_, 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) {
|
func (l *ftpDisk) Read(path string) ([]byte, error) {
|
||||||
l.stepLock.Lock()
|
l.stepLock.Lock()
|
||||||
defer l.stepLock.Unlock()
|
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)
|
f, err := l.client.Retr(path)
|
||||||
if err != nil {
|
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()
|
defer f.Close()
|
||||||
|
|
||||||
data, err := io.ReadAll(f)
|
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 {
|
func (l *ftpDisk) Write(path string, data []byte) error {
|
||||||
l.stepLock.Lock()
|
l.stepLock.Lock()
|
||||||
defer l.stepLock.Unlock()
|
defer l.stepLock.Unlock()
|
||||||
|
|
||||||
log.Debug().Str("path", path).Str("schema", "ftp").Msg("writing to file")
|
slog.Debug("writing to file", slog.String("path", path), slog.String("schema", "ftp"))
|
||||||
return errors.Wrap(l.client.Stor(path, bytes.NewReader(data)), "failed to write file")
|
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 {
|
func (l *ftpDisk) Remove(path string) error {
|
||||||
l.stepLock.Lock()
|
l.stepLock.Lock()
|
||||||
defer l.stepLock.Unlock()
|
defer l.stepLock.Unlock()
|
||||||
|
|
||||||
log.Debug().Str("path", path).Str("schema", "ftp").Msg("deleting path")
|
slog.Debug("deleting path", slog.String("path", path), slog.String("schema", "ftp"))
|
||||||
return errors.Wrap(l.client.Delete(path), "failed to delete path")
|
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 {
|
func (l *ftpDisk) MkDir(path string) error {
|
||||||
l.stepLock.Lock()
|
l.stepLock.Lock()
|
||||||
defer l.stepLock.Unlock()
|
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("/")
|
err := l.client.ChangeDir("/")
|
||||||
if err != nil {
|
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:], "/")
|
split := strings.Split(path[1:], "/")
|
||||||
|
@ -125,15 +137,15 @@ func (l *ftpDisk) MkDir(path string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !foundDir {
|
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 {
|
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 {
|
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()
|
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)
|
dir, err := l.client.List(path)
|
||||||
if err != nil {
|
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))
|
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) {
|
func (l *ftpDisk) Open(path string, _ int) (io.WriteCloser, error) {
|
||||||
reader, writer := io.Pipe()
|
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() {
|
go func() {
|
||||||
l.stepLock.Lock()
|
l.stepLock.Lock()
|
||||||
|
@ -186,9 +198,9 @@ func (l *ftpDisk) Open(path string, _ int) (io.WriteCloser, error) {
|
||||||
|
|
||||||
err := l.client.Stor(path, reader)
|
err := l.client.Stor(path, reader)
|
||||||
if err != nil {
|
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
|
return writer, nil
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package disk
|
package disk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Disk interface {
|
type Disk interface {
|
||||||
|
@ -49,18 +48,18 @@ type Entry interface {
|
||||||
func FromPath(path string) (Disk, error) {
|
func FromPath(path string) (Disk, error) {
|
||||||
parsed, err := url.Parse(path)
|
parsed, err := url.Parse(path)
|
||||||
if err != nil {
|
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 {
|
switch parsed.Scheme {
|
||||||
case "ftp":
|
case "ftp":
|
||||||
log.Info().Str("path", path).Msg("connecting to ftp")
|
slog.Info("connecting to ftp", slog.String("path", path))
|
||||||
return newFTP(path)
|
return newFTP(path)
|
||||||
case "sftp":
|
case "sftp":
|
||||||
log.Info().Str("path", path).Msg("connecting to sftp")
|
slog.Info("connecting to sftp", slog.String("path", path))
|
||||||
return newSFTP(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)
|
return newLocal(path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -10,8 +12,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
@ -50,18 +50,18 @@ func InitInstallations() (*Installations, error) {
|
||||||
_, err := os.Stat(installationsFile)
|
_, err := os.Stat(installationsFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
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)
|
_, err := os.Stat(localDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
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)
|
err = os.MkdirAll(localDir, 0o755)
|
||||||
if err != nil {
|
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 {
|
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)
|
installationsData, err := os.ReadFile(installationsFile)
|
||||||
if err != nil {
|
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
|
var installations Installations
|
||||||
if err := json.Unmarshal(installationsData, &installations); err != nil {
|
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 {
|
if installations.Version >= nextInstallationsVersion {
|
||||||
|
@ -93,21 +93,21 @@ func InitInstallations() (*Installations, error) {
|
||||||
|
|
||||||
func (i *Installations) Save() error {
|
func (i *Installations) Save() error {
|
||||||
if viper.GetBool("dry-run") {
|
if viper.GetBool("dry-run") {
|
||||||
log.Info().Msg("dry-run: skipping installation saving")
|
slog.Info("dry-run: skipping installation saving")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
installationsFile := filepath.Join(viper.GetString("local-dir"), viper.GetString("installations-file"))
|
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, "", " ")
|
installationsJSON, err := json.MarshalIndent(i, "", " ")
|
||||||
if err != nil {
|
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 {
|
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
|
return nil
|
||||||
|
@ -116,7 +116,7 @@ func (i *Installations) Save() error {
|
||||||
func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string, profile string) (*Installation, error) {
|
func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string, profile string) (*Installation, error) {
|
||||||
parsed, err := url.Parse(installPath)
|
parsed, err := url.Parse(installPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to parse path")
|
return nil, fmt.Errorf("failed to parse path: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
absolutePath := installPath
|
absolutePath := installPath
|
||||||
|
@ -124,7 +124,7 @@ func (i *Installations) AddInstallation(ctx *GlobalContext, installPath string,
|
||||||
absolutePath, err = filepath.Abs(installPath)
|
absolutePath, err = filepath.Abs(installPath)
|
||||||
|
|
||||||
if err != nil {
|
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 {
|
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
|
found := false
|
||||||
|
@ -206,7 +206,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error {
|
||||||
err = d.Exists(filepath.Join(i.BasePath(), "FactoryGame.exe"))
|
err = d.Exists(filepath.Join(i.BasePath(), "FactoryGame.exe"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !d.IsNotExist(err) {
|
if !d.IsNotExist(err) {
|
||||||
return errors.Wrap(err, "failed reading FactoryGame.exe")
|
return fmt.Errorf("failed reading FactoryGame.exe: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foundExecutable = true
|
foundExecutable = true
|
||||||
|
@ -215,7 +215,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error {
|
||||||
err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.sh"))
|
err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.sh"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !d.IsNotExist(err) {
|
if !d.IsNotExist(err) {
|
||||||
return errors.Wrap(err, "failed reading FactoryServer.sh")
|
return fmt.Errorf("failed reading FactoryServer.sh: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foundExecutable = true
|
foundExecutable = true
|
||||||
|
@ -224,7 +224,7 @@ func (i *Installation) Validate(ctx *GlobalContext) error {
|
||||||
err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.exe"))
|
err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.exe"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !d.IsNotExist(err) {
|
if !d.IsNotExist(err) {
|
||||||
return errors.Wrap(err, "failed reading FactoryServer.exe")
|
return fmt.Errorf("failed reading FactoryServer.exe: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foundExecutable = true
|
foundExecutable = true
|
||||||
|
@ -273,11 +273,11 @@ func (i *Installation) LockFile(ctx *GlobalContext) (*resolver.LockFile, error)
|
||||||
lockFileJSON, err := d.Read(lockfilePath)
|
lockFileJSON, err := d.Read(lockfilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !d.IsNotExist(err) {
|
if !d.IsNotExist(err) {
|
||||||
return nil, errors.Wrap(err, "failed reading lockfile")
|
return nil, fmt.Errorf("failed reading lockfile: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := json.Unmarshal(lockFileJSON, &lockFile); err != nil {
|
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)
|
lockfileDir := filepath.Dir(lockfilePath)
|
||||||
if err := d.Exists(lockfileDir); d.IsNotExist(err) {
|
if err := d.Exists(lockfileDir); d.IsNotExist(err) {
|
||||||
if err := d.MkDir(lockfileDir); err != nil {
|
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, "", " ")
|
marshaledLockfile, err := json.MarshalIndent(lockfile, "", " ")
|
||||||
if err != nil {
|
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 {
|
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
|
return nil
|
||||||
|
@ -322,7 +322,7 @@ func (i *Installation) Wipe() error {
|
||||||
|
|
||||||
modsDirectory := filepath.Join(i.BasePath(), "FactoryGame", "Mods")
|
modsDirectory := filepath.Join(i.BasePath(), "FactoryGame", "Mods")
|
||||||
if err := d.Remove(modsDirectory); err != nil {
|
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
|
return nil
|
||||||
|
@ -338,16 +338,16 @@ func (i *Installation) ResolveProfile(ctx *GlobalContext) (*resolver.LockFile, e
|
||||||
|
|
||||||
gameVersion, err := i.GetGameVersion(ctx)
|
gameVersion, err := i.GetGameVersion(ctx)
|
||||||
if err != nil {
|
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)
|
lockfile, err := ctx.Profiles.Profiles[i.Profile].Resolve(depResolver, lockFile, gameVersion)
|
||||||
if err != nil {
|
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 {
|
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
|
return lockfile, nil
|
||||||
|
@ -375,12 +375,12 @@ type InstallUpdateItem struct {
|
||||||
|
|
||||||
func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) error {
|
func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate) error {
|
||||||
if err := i.Validate(ctx); err != nil {
|
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)
|
platform, err := i.GetPlatform(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to detect platform")
|
return fmt.Errorf("failed to detect platform: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
lockfile := resolver.NewLockfile()
|
lockfile := resolver.NewLockfile()
|
||||||
|
@ -389,7 +389,7 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
|
||||||
var err error
|
var err error
|
||||||
lockfile, err = i.ResolveProfile(ctx)
|
lockfile, err = i.ResolveProfile(ctx)
|
||||||
if err != nil {
|
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")
|
modsDirectory := filepath.Join(i.BasePath(), "FactoryGame", "Mods")
|
||||||
if err := d.MkDir(modsDirectory); err != nil {
|
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)
|
dir, err := d.ReadDir(modsDirectory)
|
||||||
if err != nil {
|
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 {
|
for _, entry := range dir {
|
||||||
|
@ -414,16 +414,16 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
|
||||||
modDir := filepath.Join(modsDirectory, entry.Name())
|
modDir := filepath.Join(modsDirectory, entry.Name())
|
||||||
err := d.Exists(filepath.Join(modDir, ".smm"))
|
err := d.Exists(filepath.Join(modDir, ".smm"))
|
||||||
if err == nil {
|
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 {
|
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{}
|
errg := errgroup.Group{}
|
||||||
channelUsers := sync.WaitGroup{}
|
channelUsers := sync.WaitGroup{}
|
||||||
|
@ -461,14 +461,14 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
|
||||||
|
|
||||||
target, ok := version.Targets[platform.TargetName]
|
target, ok := version.Targets[platform.TargetName]
|
||||||
if !ok {
|
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
|
// Only install if a link is provided, otherwise assume mod is already installed
|
||||||
if target.Link != "" {
|
if target.Link != "" {
|
||||||
err := downloadAndExtractMod(modReference, version.Version, target.Link, target.Hash, modsDirectory, updates, downloadSemaphore, d)
|
err := downloadAndExtractMod(modReference, version.Version, target.Link, target.Hash, modsDirectory, updates, downloadSemaphore, d)
|
||||||
if err != nil {
|
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 {
|
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
|
return nil
|
||||||
|
@ -495,19 +495,19 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
|
||||||
|
|
||||||
func (i *Installation) UpdateMods(ctx *GlobalContext, mods []string) error {
|
func (i *Installation) UpdateMods(ctx *GlobalContext, mods []string) error {
|
||||||
if err := i.Validate(ctx); err != nil {
|
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)
|
lockFile, err := i.LockFile(ctx)
|
||||||
if err != nil {
|
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"))
|
resolver := resolver.NewDependencyResolver(ctx.Provider, viper.GetString("api-base"))
|
||||||
|
|
||||||
gameVersion, err := i.GetGameVersion(ctx)
|
gameVersion, err := i.GetGameVersion(ctx)
|
||||||
if err != nil {
|
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)
|
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)
|
newLockFile, err := profile.Resolve(resolver, lockFile, gameVersion)
|
||||||
if err != nil {
|
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 {
|
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
|
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)
|
reader, size, err := cache.DownloadOrCache(modReference+"_"+version+".zip", hash, link, downloadUpdates, downloadSemaphore)
|
||||||
if err != nil {
|
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()
|
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 {
|
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 {
|
if updates != nil {
|
||||||
|
@ -638,7 +638,7 @@ type gameVersionFile struct {
|
||||||
|
|
||||||
func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
|
func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
|
||||||
if err := i.Validate(ctx); err != nil {
|
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)
|
platform, err := i.GetPlatform(ctx)
|
||||||
|
@ -655,14 +655,14 @@ func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
|
||||||
file, err := d.Read(fullPath)
|
file, err := d.Read(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if d.IsNotExist(err) {
|
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
|
var versionData gameVersionFile
|
||||||
if err := json.Unmarshal(file, &versionData); err != nil {
|
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
|
return versionData.Changelist, nil
|
||||||
|
@ -670,7 +670,7 @@ func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
|
||||||
|
|
||||||
func (i *Installation) GetPlatform(ctx *GlobalContext) (*Platform, error) {
|
func (i *Installation) GetPlatform(ctx *GlobalContext) (*Platform, error) {
|
||||||
if err := i.Validate(ctx); err != nil {
|
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()
|
d, err := i.GetDisk()
|
||||||
|
@ -685,7 +685,7 @@ func (i *Installation) GetPlatform(ctx *GlobalContext) (*Platform, error) {
|
||||||
if d.IsNotExist(err) {
|
if d.IsNotExist(err) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, errors.Wrap(err, "failed detecting version file")
|
return nil, fmt.Errorf("failed detecting version file: %w", err)
|
||||||
}
|
}
|
||||||
return &platform, nil
|
return &platform, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@ package cli
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -62,18 +62,18 @@ func InitProfiles() (*Profiles, error) {
|
||||||
_, err := os.Stat(profilesFile)
|
_, err := os.Stat(profilesFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
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)
|
_, err := os.Stat(localDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
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)
|
err = os.MkdirAll(localDir, 0o755)
|
||||||
if err != nil {
|
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 {
|
if err == nil {
|
||||||
manifestBytes, err := os.ReadFile(manifestFile)
|
manifestBytes, err := os.ReadFile(manifestFile)
|
||||||
if err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var smmProfile smmProfileFile
|
var smmProfile smmProfileFile
|
||||||
if err := json.Unmarshal(manifestBytes, &smmProfile); err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,18 +134,18 @@ func InitProfiles() (*Profiles, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bootstrapProfiles.Save(); err != nil {
|
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)
|
profilesData, err := os.ReadFile(profilesFile)
|
||||||
if err != nil {
|
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
|
var profiles Profiles
|
||||||
if err := json.Unmarshal(profilesData, &profiles); err != nil {
|
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 {
|
if profiles.Version >= nextProfilesVersion {
|
||||||
|
@ -169,21 +169,21 @@ func InitProfiles() (*Profiles, error) {
|
||||||
// Save the profiles to the profiles file.
|
// Save the profiles to the profiles file.
|
||||||
func (p *Profiles) Save() error {
|
func (p *Profiles) Save() error {
|
||||||
if viper.GetBool("dry-run") {
|
if viper.GetBool("dry-run") {
|
||||||
log.Info().Msg("dry-run: skipping profile saving")
|
slog.Info("dry-run: skipping profile saving")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
profilesFile := filepath.Join(viper.GetString("local-dir"), viper.GetString("profiles-file"))
|
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, "", " ")
|
profilesJSON, err := json.MarshalIndent(p, "", " ")
|
||||||
if err != nil {
|
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 {
|
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
|
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)
|
resultLockfile, err := resolver.ResolveModDependencies(context.TODO(), toResolve, lockFile, gameVersion, p.RequiredTargets)
|
||||||
if err != nil {
|
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
|
return resultLockfile, nil
|
||||||
|
|
|
@ -2,9 +2,9 @@ package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/Khan/genqlient/graphql"
|
"github.com/Khan/genqlient/graphql"
|
||||||
"github.com/pkg/errors"
|
|
||||||
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/ficsit"
|
"github.com/satisfactorymodding/ficsit-cli/ficsit"
|
||||||
|
|
|
@ -2,10 +2,11 @@ package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/cli/cache"
|
"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) {
|
func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit.ModsResponse, error) {
|
||||||
cachedMods, err := cache.GetCache()
|
cachedMods, err := cache.GetCache()
|
||||||
if err != nil {
|
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)
|
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) {
|
func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.GetModResponse, error) {
|
||||||
cachedModFiles, err := cache.GetCacheMod(modReference)
|
cachedModFiles, err := cache.GetCacheMod(modReference)
|
||||||
if err != nil {
|
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 {
|
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) {
|
func (p LocalProvider) SMLVersions(_ context.Context) ([]resolver.SMLVersion, error) {
|
||||||
cachedSMLFiles, err := cache.GetCacheMod("SML")
|
cachedSMLFiles, err := cache.GetCacheMod("SML")
|
||||||
if err != nil {
|
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)
|
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) {
|
func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) {
|
||||||
cachedModFiles, err := cache.GetCacheMod(modID)
|
cachedModFiles, err := cache.GetCacheMod(modID)
|
||||||
if err != nil {
|
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)
|
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) {
|
func (p LocalProvider) GetModName(_ context.Context, modReference string) (*resolver.ModName, error) {
|
||||||
cachedModFiles, err := cache.GetCacheMod(modReference)
|
cachedModFiles, err := cache.GetCacheMod(modReference)
|
||||||
if err != nil {
|
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 {
|
if len(cachedModFiles) == 0 {
|
||||||
|
|
|
@ -2,12 +2,12 @@ package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/MarvinJWendt/testza"
|
"github.com/MarvinJWendt/testza"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ func installWatcher() chan<- InstallUpdate {
|
||||||
for i := range c {
|
for i := range c {
|
||||||
if i.Progress.Total == i.Progress.Completed {
|
if i.Progress.Total == i.Progress.Completed {
|
||||||
if i.Type != InstallUpdateTypeOverall {
|
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 {
|
} else {
|
||||||
log.Info().Msg("overall completed")
|
slog.Info("overall completed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
cmd/cli.go
12
cmd/cli.go
|
@ -1,7 +1,8 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/rs/zerolog/log"
|
"log/slog"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -13,10 +14,11 @@ var cliCmd = &cobra.Command{
|
||||||
Use: "cli",
|
Use: "cli",
|
||||||
Short: "Start interactive CLI (default)",
|
Short: "Start interactive CLI (default)",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
log.Info().
|
slog.Info(
|
||||||
Str("version", viper.GetString("version")).
|
"interactive cli initialized",
|
||||||
Str("commit", viper.GetString("commit")).
|
slog.String("version", viper.GetString("version")),
|
||||||
Msg("interactive cli initialized")
|
slog.String("commit", viper.GetString("commit")),
|
||||||
|
)
|
||||||
|
|
||||||
global, err := cli.InitCLI(false)
|
global, err := cli.InitCLI(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package installation
|
package installation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pkg/errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/cli"
|
"github.com/satisfactorymodding/ficsit-cli/cli"
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package installation
|
package installation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pkg/errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package profile
|
package profile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pkg/errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/cli"
|
"github.com/satisfactorymodding/ficsit-cli/cli"
|
||||||
|
|
57
cmd/root.go
57
cmd/root.go
|
@ -1,16 +1,16 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/lmittmann/tint"
|
||||||
"github.com/pterm/pterm"
|
"github.com/pterm/pterm"
|
||||||
"github.com/rs/zerolog"
|
slogmulti "github.com/samber/slog-multi"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -31,37 +31,56 @@ var RootCmd = &cobra.Command{
|
||||||
|
|
||||||
_ = viper.ReadInConfig()
|
_ = viper.ReadInConfig()
|
||||||
|
|
||||||
level, err := zerolog.ParseLevel(viper.GetString("log"))
|
handlers := make([]slog.Handler, 0)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
zerolog.SetGlobalLevel(level)
|
|
||||||
|
|
||||||
writers := make([]io.Writer, 0)
|
|
||||||
if viper.GetBool("pretty") {
|
if viper.GetBool("pretty") {
|
||||||
pterm.EnableStyling()
|
pterm.EnableStyling()
|
||||||
} else {
|
} else {
|
||||||
pterm.DisableStyling()
|
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") {
|
if !viper.GetBool("quiet") {
|
||||||
writers = append(writers, zerolog.ConsoleWriter{
|
handlers = append(handlers, tint.NewHandler(os.Stdout, &tint.Options{
|
||||||
Out: os.Stdout,
|
Level: level,
|
||||||
TimeFormat: time.RFC3339,
|
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") != "" {
|
if viper.GetString("log-file") != "" {
|
||||||
logFile, err := os.OpenFile(viper.GetString("log-file"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o777)
|
logFile, err := os.OpenFile(viper.GetString("log-file"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o777)
|
||||||
if err != nil {
|
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
|
return nil
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@ var searchCmd = &cobra.Command{
|
||||||
case "json":
|
case "json":
|
||||||
result, err := json.MarshalIndent(modList, "", " ")
|
result, err := json.MarshalIndent(modList, "", " ")
|
||||||
if err != nil {
|
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))
|
println(string(result))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@ package smr
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -12,8 +15,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ var uploadCmd = &cobra.Command{
|
||||||
|
|
||||||
stat, err := os.Stat(filePath)
|
stat, err := os.Stat(filePath)
|
||||||
if err != nil {
|
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)
|
global, err := cli.InitCLI(true)
|
||||||
|
@ -71,31 +72,31 @@ var uploadCmd = &cobra.Command{
|
||||||
|
|
||||||
// TODO Validate .smod file before upload
|
// TODO Validate .smod file before upload
|
||||||
|
|
||||||
logBase := log.With().Str("mod-id", modID).Str("path", filePath).Logger()
|
logBase := slog.With(slog.String("mod-id", modID), slog.String("path", filePath))
|
||||||
logBase.Info().Msg("creating a new mod version")
|
logBase.Info("creating a new mod version")
|
||||||
|
|
||||||
createdVersion, err := ficsit.CreateVersion(cmd.Context(), global.APIClient, modID)
|
createdVersion, err := ficsit.CreateVersion(cmd.Context(), global.APIClient, modID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logBase = logBase.With().Str("version-id", createdVersion.GetVersionID()).Logger()
|
logBase = logBase.With(slog.String("version-id", createdVersion.GetVersionID()))
|
||||||
logBase.Info().Msg("received version id")
|
logBase.Info("received version id")
|
||||||
|
|
||||||
// TODO Parallelize chunk uploading
|
// TODO Parallelize chunk uploading
|
||||||
chunkCount := int(math.Ceil(float64(stat.Size()) / float64(chunkSize)))
|
chunkCount := int(math.Ceil(float64(stat.Size()) / float64(chunkSize)))
|
||||||
for i := 0; i < chunkCount; i++ {
|
for i := 0; i < chunkCount; i++ {
|
||||||
chunkLog := logBase.With().Int("chunk", i).Logger()
|
chunkLog := logBase.With(slog.Int("chunk", i))
|
||||||
chunkLog.Info().Msg("uploading chunk")
|
chunkLog.Info("uploading chunk")
|
||||||
|
|
||||||
f, err := os.Open(filePath)
|
f, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to open file")
|
return fmt.Errorf("failed to open file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := int64(i) * chunkSize
|
offset := int64(i) * chunkSize
|
||||||
if _, err := f.Seek(offset, 0); err != nil {
|
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
|
bufferSize := chunkSize
|
||||||
|
@ -105,7 +106,7 @@ var uploadCmd = &cobra.Command{
|
||||||
|
|
||||||
chunk := make([]byte, bufferSize)
|
chunk := make([]byte, bufferSize)
|
||||||
if _, err := f.Read(chunk); err != nil {
|
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{}{
|
operationBody, err := json.Marshal(map[string]interface{}{
|
||||||
|
@ -118,14 +119,14 @@ var uploadCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
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{}{
|
mapBody, err := json.Marshal(map[string]interface{}{
|
||||||
"0": []string{"variables.file"},
|
"0": []string{"variables.file"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
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{}
|
body := &bytes.Buffer{}
|
||||||
|
@ -133,33 +134,33 @@ var uploadCmd = &cobra.Command{
|
||||||
|
|
||||||
operations, err := writer.CreateFormField("operations")
|
operations, err := writer.CreateFormField("operations")
|
||||||
if err != nil {
|
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 {
|
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")
|
mapField, err := writer.CreateFormField("map")
|
||||||
if err != nil {
|
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 {
|
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))
|
part, err := writer.CreateFormFile("0", filepath.Base(filePath))
|
||||||
if err != nil {
|
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 {
|
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 {
|
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)
|
r, _ := http.NewRequest("POST", viper.GetString("api-base")+viper.GetString("graphql-api"), body)
|
||||||
|
@ -168,12 +169,12 @@ var uploadCmd = &cobra.Command{
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
if _, err := client.Do(r); err != nil {
|
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{
|
finalizeSuccess, err := ficsit.FinalizeCreateVersion(cmd.Context(), global.APIClient, modID, createdVersion.GetVersionID(), ficsit.NewVersion{
|
||||||
Changelog: changelog,
|
Changelog: changelog,
|
||||||
|
@ -184,16 +185,16 @@ var uploadCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
if !finalizeSuccess.GetSuccess() {
|
if !finalizeSuccess.GetSuccess() {
|
||||||
logBase.Error().Msg("failed to finalize version upload")
|
logBase.Error("failed to finalize version upload")
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(time.Second * 1)
|
time.Sleep(time.Second * 1)
|
||||||
|
|
||||||
for {
|
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())
|
state, err := ficsit.CheckVersionUploadState(cmd.Context(), global.APIClient, modID, createdVersion.GetVersionID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logBase.Err(err).Msg("failed to upload mod")
|
logBase.Error("failed to upload mod", slog.Any("err", err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,11 +204,11 @@ var uploadCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.GetState().Auto_approved {
|
if state.GetState().Auto_approved {
|
||||||
logBase.Info().Msg("version successfully uploaded and auto-approved")
|
logBase.Info("version successfully uploaded and auto-approved")
|
||||||
break
|
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
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package ficsit
|
package ficsit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Khan/genqlient/graphql"
|
"github.com/Khan/genqlient/graphql"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,7 +19,11 @@ func (t *AuthedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rt, err := t.Wrapped.RoundTrip(req)
|
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 {
|
func InitAPI() graphql.Client {
|
||||||
|
|
|
@ -2,9 +2,8 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//goland:noinspection GoUnusedExportedFunction
|
//goland:noinspection GoUnusedExportedFunction
|
||||||
|
@ -18,7 +17,7 @@ func UnmarshalDateTime(b []byte, v *time.Time) error {
|
||||||
|
|
||||||
parsed, err := time.Parse(time.RFC3339, string(trimmed))
|
parsed, err := time.Parse(time.RFC3339, string(trimmed))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to parse date time")
|
return fmt.Errorf("failed to parse date time: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
*v = parsed
|
*v = parsed
|
||||||
|
|
19
go.mod
19
go.mod
|
@ -9,21 +9,21 @@ require (
|
||||||
github.com/Khan/genqlient v0.6.0
|
github.com/Khan/genqlient v0.6.0
|
||||||
github.com/MarvinJWendt/testza v0.5.2
|
github.com/MarvinJWendt/testza v0.5.2
|
||||||
github.com/PuerkitoBio/goquery v1.8.1
|
github.com/PuerkitoBio/goquery v1.8.1
|
||||||
github.com/charmbracelet/bubbles v0.16.1
|
github.com/charmbracelet/bubbles v0.17.1
|
||||||
github.com/charmbracelet/bubbletea v0.24.2
|
github.com/charmbracelet/bubbletea v0.25.0
|
||||||
github.com/charmbracelet/glamour v0.6.0
|
github.com/charmbracelet/glamour v0.6.0
|
||||||
github.com/charmbracelet/lipgloss v0.9.1
|
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/jlaffaye/ftp v0.2.0
|
||||||
|
github.com/lmittmann/tint v1.0.3
|
||||||
github.com/muesli/reflow v0.3.0
|
github.com/muesli/reflow v0.3.0
|
||||||
github.com/pkg/errors v0.9.1
|
|
||||||
github.com/pterm/pterm v0.12.71
|
github.com/pterm/pterm v0.12.71
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.0.2
|
github.com/puzpuzpuz/xsync/v3 v3.0.2
|
||||||
github.com/rs/zerolog v1.31.0
|
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f
|
||||||
github.com/sahilm/fuzzy v0.1.0
|
github.com/samber/slog-multi v1.0.2
|
||||||
github.com/satisfactorymodding/ficsit-resolver v0.0.2
|
github.com/satisfactorymodding/ficsit-resolver v0.0.2
|
||||||
github.com/spf13/cobra v1.8.0
|
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
|
golang.org/x/sync v0.5.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ require (
|
||||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // 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/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // 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/lithammer/fuzzysearch v1.1.8 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // 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-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // 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/muesli/termenv v0.15.2 // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 // 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/rivo/uniseg v0.4.4 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.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/sergi/go-diff v1.3.1 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
|
39
go.sum
39
go.sum
|
@ -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 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 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
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.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||||
github.com/aymanbagabas/go-udiff v0.1.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
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 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
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 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs=
|
||||||
github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
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.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4=
|
||||||
github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
|
github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o=
|
||||||
github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY=
|
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
||||||
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
|
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 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
|
||||||
github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc=
|
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 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
||||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
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 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
||||||
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
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-20231215171016-7ba2b450712d h1:J6mdY8xl7YVGMSbPlqDcg64/J3m7wPuX1OWzPMWW4OA=
|
||||||
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/go.mod h1:43J0pdacLjJQtomu7vU6RFZX3bn84toqNw7hjX8bhmM=
|
||||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
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 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
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 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
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=
|
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/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 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
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 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
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=
|
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/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 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
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 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
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 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
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.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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
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=
|
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/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 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
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 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
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 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
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 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
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.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
|
||||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
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 h1:dj/OsDLpaMUqCHpfBVHvDMUv2nf5gT4HS2ydBMkmtcQ=
|
||||||
github.com/satisfactorymodding/ficsit-resolver v0.0.2/go.mod h1:ckKMmMvDoYbbkEbWXEsMes608uvv6EKphXPhHX8LKSc=
|
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=
|
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/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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
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.1 h1:rmuU42rScKWlhhJDyXZRKJQHXFX02chSVW1IvkPGiVM=
|
||||||
github.com/spf13/viper v1.18.0/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
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.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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.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.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.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
package tea
|
package tea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/Khan/genqlient/graphql"
|
"github.com/Khan/genqlient/graphql"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/pkg/errors"
|
|
||||||
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
resolver "github.com/satisfactorymodding/ficsit-resolver"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ func (m *rootModel) SetCurrentProfile(profile *cli.Profile) error {
|
||||||
|
|
||||||
if m.GetCurrentInstallation() != nil {
|
if m.GetCurrentInstallation() != nil {
|
||||||
if err := m.GetCurrentInstallation().SetProfile(m.global, profile.Name); err != 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 {
|
func RunTea(global *cli.GlobalContext) error {
|
||||||
if _, err := tea.NewProgram(scenes.NewMainMenu(newModel(global)), tea.WithAltScreen(), tea.WithMouseCellMotion()).Run(); err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,11 @@ var _ tea.Model = (*newInstallation)(nil)
|
||||||
|
|
||||||
type newInstallation struct {
|
type newInstallation struct {
|
||||||
dirList list.Model
|
dirList list.Model
|
||||||
input textinput.Model
|
|
||||||
root components.RootModel
|
root components.RootModel
|
||||||
parent tea.Model
|
parent tea.Model
|
||||||
error *components.ErrorComponent
|
error *components.ErrorComponent
|
||||||
title string
|
title string
|
||||||
|
input textinput.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNewInstallation(root components.RootModel, parent tea.Model) tea.Model {
|
func NewNewInstallation(root components.RootModel, parent tea.Model) tea.Model {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package scenes
|
package scenes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -8,7 +9,6 @@ import (
|
||||||
"github.com/charmbracelet/bubbles/list"
|
"github.com/charmbracelet/bubbles/list"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||||
|
@ -114,7 +114,7 @@ func NewMainMenu(root components.RootModel) tea.Model {
|
||||||
ItemTitle: "Apply Changes",
|
ItemTitle: "Apply Changes",
|
||||||
Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) {
|
Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) {
|
||||||
if err := root.GetGlobal().Save(); err != nil {
|
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)
|
errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5)
|
||||||
currentModel.error = errorComponent
|
currentModel.error = errorComponent
|
||||||
return currentModel, cmd
|
return currentModel, cmd
|
||||||
|
@ -128,7 +128,7 @@ func NewMainMenu(root components.RootModel) tea.Model {
|
||||||
ItemTitle: "Save",
|
ItemTitle: "Save",
|
||||||
Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) {
|
Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) {
|
||||||
if err := root.GetGlobal().Save(); err != nil {
|
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)
|
errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5)
|
||||||
currentModel.error = errorComponent
|
currentModel.error = errorComponent
|
||||||
return currentModel, cmd
|
return currentModel, cmd
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package mods
|
package mods
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
"github.com/charmbracelet/bubbles/list"
|
"github.com/charmbracelet/bubbles/list"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||||
"github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors"
|
"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) {
|
Activate: func(msg tea.Msg, currentModel modMenu) (tea.Model, tea.Cmd) {
|
||||||
err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0")
|
err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg(errors.ErrorFailedAddMod)
|
slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err))
|
||||||
cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod)
|
cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod)
|
||||||
return currentModel, cmd
|
return currentModel, cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package mods
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -15,7 +16,6 @@ import (
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/glamour"
|
"github.com/charmbracelet/glamour"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/ficsit"
|
"github.com/satisfactorymodding/ficsit-cli/ficsit"
|
||||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
"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)
|
markdownDescription, err := converter.ConvertString(mod.Full_description)
|
||||||
if err != nil {
|
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
|
markdownDescription = mod.Full_description
|
||||||
}
|
}
|
||||||
|
|
||||||
description, err := glamour.Render(markdownDescription, "dark")
|
description, err := glamour.Render(markdownDescription, "dark")
|
||||||
if err != nil {
|
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
|
description = mod.Full_description
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,12 @@ import (
|
||||||
var _ tea.Model = (*modSemver)(nil)
|
var _ tea.Model = (*modSemver)(nil)
|
||||||
|
|
||||||
type modSemver struct {
|
type modSemver struct {
|
||||||
input textinput.Model
|
|
||||||
root components.RootModel
|
root components.RootModel
|
||||||
parent tea.Model
|
parent tea.Model
|
||||||
error *components.ErrorComponent
|
error *components.ErrorComponent
|
||||||
mod utils.Mod
|
mod utils.Mod
|
||||||
title string
|
title string
|
||||||
|
input textinput.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModSemver(root components.RootModel, parent tea.Model, mod utils.Mod) tea.Model {
|
func NewModSemver(root components.RootModel, parent tea.Model, mod utils.Mod) tea.Model {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package mods
|
package mods
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
"github.com/charmbracelet/bubbles/list"
|
"github.com/charmbracelet/bubbles/list"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
"github.com/satisfactorymodding/ficsit-cli/tea/components"
|
||||||
"github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors"
|
"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) {
|
Activate: func(msg tea.Msg, currentModel modVersionMenu) (tea.Model, tea.Cmd) {
|
||||||
err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0")
|
err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg(errors.ErrorFailedAddMod)
|
slog.Error(errors.ErrorFailedAddMod, slog.Any("err", err))
|
||||||
cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod)
|
cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod)
|
||||||
return currentModel, cmd
|
return currentModel, cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,13 +33,13 @@ func (k keyMap) FullHelp() [][]key.Binding {
|
||||||
}
|
}
|
||||||
|
|
||||||
type newProfile struct {
|
type newProfile struct {
|
||||||
input textinput.Model
|
|
||||||
root components.RootModel
|
root components.RootModel
|
||||||
parent tea.Model
|
parent tea.Model
|
||||||
error *components.ErrorComponent
|
error *components.ErrorComponent
|
||||||
title string
|
|
||||||
help help.Model
|
help help.Model
|
||||||
|
title string
|
||||||
keys keyMap
|
keys keyMap
|
||||||
|
input textinput.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNewProfile(root components.RootModel, parent tea.Model) tea.Model {
|
func NewNewProfile(root components.RootModel, parent tea.Model) tea.Model {
|
||||||
|
|
|
@ -17,12 +17,12 @@ import (
|
||||||
var _ tea.Model = (*renameProfile)(nil)
|
var _ tea.Model = (*renameProfile)(nil)
|
||||||
|
|
||||||
type renameProfile struct {
|
type renameProfile struct {
|
||||||
input textinput.Model
|
|
||||||
root components.RootModel
|
root components.RootModel
|
||||||
parent tea.Model
|
parent tea.Model
|
||||||
error *components.ErrorComponent
|
error *components.ErrorComponent
|
||||||
title string
|
title string
|
||||||
oldName string
|
oldName string
|
||||||
|
input textinput.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRenameProfile(root components.RootModel, parent tea.Model, profileData *cli.Profile) tea.Model {
|
func NewRenameProfile(root components.RootModel, parent tea.Model, profileData *cli.Profile) tea.Model {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package tea
|
package tea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -11,7 +12,6 @@ import (
|
||||||
"github.com/MarvinJWendt/testza"
|
"github.com/MarvinJWendt/testza"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/x/exp/teatest"
|
"github.com/charmbracelet/x/exp/teatest"
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/cfg"
|
"github.com/satisfactorymodding/ficsit-cli/cfg"
|
||||||
"github.com/satisfactorymodding/ficsit-cli/cli"
|
"github.com/satisfactorymodding/ficsit-cli/cli"
|
||||||
|
|
31
utils/io.go
31
utils/io.go
|
@ -4,14 +4,13 @@ import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/satisfactorymodding/ficsit-cli/cli/disk"
|
"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, 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) {
|
func SHA256Data(f io.Reader) (string, error) {
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
if _, err := io.Copy(h, f); err != nil {
|
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
|
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)
|
hashBytes, err := d.Read(hashFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !d.IsNotExist(err) {
|
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 {
|
} else {
|
||||||
if hash == string(hashBytes) {
|
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 err := d.MkDir(location); err != nil {
|
||||||
if !d.IsExist(err) {
|
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 {
|
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 {
|
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)
|
reader, err := zip.NewReader(f, size)
|
||||||
if err != nil {
|
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)
|
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)
|
outFileLocation := filepath.Join(location, file.Name)
|
||||||
|
|
||||||
if err := d.MkDir(filepath.Dir(outFileLocation)); err != nil {
|
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
|
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 {
|
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 {
|
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)
|
outFile, err := d.Open(outFileLocation, os.O_CREATE|os.O_RDWR)
|
||||||
if err != nil {
|
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()
|
defer outFile.Close()
|
||||||
|
|
||||||
inFile, err := file.Open()
|
inFile, err := file.Open()
|
||||||
if err != nil {
|
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()
|
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 {
|
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
|
return nil
|
||||||
|
|
|
@ -2,19 +2,18 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Copy[T any](obj T) (*T, error) {
|
func Copy[T any](obj T) (*T, error) {
|
||||||
marshal, err := json.Marshal(obj)
|
marshal, err := json.Marshal(obj)
|
||||||
if err != nil {
|
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)
|
out := new(T)
|
||||||
if err := json.Unmarshal(marshal, out); err != nil {
|
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
|
return out, nil
|
||||||
|
|
Loading…
Reference in a new issue