Compare commits

..

No commits in common. "9045893b1dd7c70e12dd1085db1b990c9ac86c0e" and "5e9fe2aebbd51439aa2eeeddaa2c8d70adaa791a" have entirely different histories.

60 changed files with 1069 additions and 1506 deletions

View file

@ -1,28 +0,0 @@
root = true
["*"]
charset = "utf-8"
curly_bracket_next_line = true
end_of_line = "lf"
indent_brace_style = "K&R"
indent_size = 4
indent_style = "space"
insert_final_newline = true
max_line_length = 80
tab_width = 4
trim_trailing_whitespace = true
["*.md"]
max_line_length = "off"
["package.json"]
indent_size = 2
indent_style = "space"
tab_width = 2
["{LICENSES/**,LICENSE}"]
charset = "unset"
end_of_line = "unset"
indent_size = "unset"
indent_style = "unset"
insert_final_newline = "unset"
trim_trailing_whitespace = "unset"

View file

@ -96,7 +96,7 @@ jobs:
- name: Boot ftp and sftp - name: Boot ftp and sftp
if: ${{ matrix.os == 'ubuntu-latest' }} if: ${{ matrix.os == 'ubuntu-latest' }}
run: docker compose -f docker-compose-test.yml up -d run: docker-compose -f docker-compose-test.yml up -d
- name: Download GQL schema - name: Download GQL schema
run: "npx graphqurl https://api.ficsit.dev/v2/query --introspect -H 'content-type: application/json' > schema.graphql" run: "npx graphqurl https://api.ficsit.dev/v2/query --introspect -H 'content-type: application/json' > schema.graphql"

View file

@ -42,7 +42,7 @@ jobs:
uses: goreleaser/goreleaser-action@v2 uses: goreleaser/goreleaser-action@v2
with: with:
version: latest version: latest
args: release --clean args: release --rm-dist --debug
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }} AUR_KEY: ${{ secrets.AUR_KEY }}

7
.gitignore vendored
View file

@ -130,10 +130,3 @@ schema.graphql
*.log *.log
.direnv .direnv
/SatisfactoryDedicatedServer /SatisfactoryDedicatedServer
# nixago: ignore-linked-files
/treefmt.toml
/.prettierrc.json
/lefthook.yml
/.conform.yaml
# mfgames-project-setup: ignore-files
/.direnv/

View file

@ -29,8 +29,11 @@ builds:
- windows - windows
goarch: goarch:
- amd64 - amd64
- arm
- arm64 - arm64
- 386 - 386
goarm:
- 7
universal_binaries: universal_binaries:
- replace: true - replace: true

View file

@ -1,10 +1,8 @@
<!-- markdownlint-disable MD033 --> <img align="right" width="310" src="./.github/screenshot.png" />
<!-- markdownlint-disable MD041 -->
<img align="right" width="310" src="./.github/screenshot.png" alt="ficsit-cli screenshot" />
# ficsit-cli [![push](https://github.com/satisfactorymodding/ficsit-cli/actions/workflows/push.yaml/badge.svg)](https://github.com/satisfactorymodding/ficsit-cli/actions/workflows/push.yaml) ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/satisfactorymodding/ficsit-cli) ![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/satisfactorymodding/ficsit-cli) [![GitHub license](https://img.shields.io/github/license/satisfactorymodding/ficsit-cli)](https://github.com/satisfactorymodding/ficsit-cli/blob/master/LICENSE) ![GitHub all releases](https://img.shields.io/github/downloads/satisfactorymodding/ficsit-cli/total) # ficsit-cli [![push](https://github.com/Vilsol/ficsit-cli/actions/workflows/push.yaml/badge.svg)](https://github.com/Vilsol/ficsit-cli/actions/workflows/push.yaml) ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/vilsol/ficsit-cli) ![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/vilsol/ficsit-cli) [![GitHub license](https://img.shields.io/github/license/Vilsol/ficsit-cli)](https://github.com/Vilsol/ficsit-cli/blob/master/LICENSE) ![GitHub all releases](https://img.shields.io/github/downloads/vilsol/ficsit-cli/total)
A CLI tool for managing mods for the game [Satisfactory](https://www.satisfactorygame.com/). A CLI tool for managing mods for the game Satisfactory
--- ---
@ -21,10 +19,10 @@ A CLI tool for managing mods for the game [Satisfactory](https://www.satisfactor
</tr> </tr>
<tr> <tr>
<th>Windows</th> <th>Windows</th>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_windows_amd64.exe">amd64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_windows_amd64.exe">amd64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_windows_386.exe">386</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_windows_386.exe">386</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_windows_arm64.exe">arm64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_windows_arm64.exe">arm64</a></td>
<td>N/A</td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_windows_armv7.exe">armv7</a></td>
<td>N/A</td> <td>N/A</td>
</tr> </tr>
<tr> <tr>
@ -33,39 +31,39 @@ A CLI tool for managing mods for the game [Satisfactory](https://www.satisfactor
</tr> </tr>
<tr> <tr>
<th>Debian</th> <th>Debian</th>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_amd64.deb">amd64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_amd64.deb">amd64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_386.deb">386</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_386.deb">386</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_arm64.deb">arm64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_arm64.deb">arm64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_armv7.deb">armv7</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_armv7.deb">armv7</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_ppc64le.deb">ppc64le</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_ppc64le.deb">ppc64le</a></td>
</tr> </tr>
<tr> <tr>
<th>Fedora</th> <th>Fedora</th>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_amd64.rpm">amd64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_amd64.rpm">amd64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_386.rpm">386</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_386.rpm">386</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_arm64.rpm">arm64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_arm64.rpm">arm64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_armv7.rpm">armv7</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_armv7.rpm">armv7</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_ppc64le.rpm">ppc64le</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_ppc64le.rpm">ppc64le</a></td>
</tr> </tr>
<tr> <tr>
<th>Alpine</th> <th>Alpine</th>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_amd64.apk">amd64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_amd64.apk">amd64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_386.apk">386</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_386.apk">386</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_arm64.apk">arm64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_arm64.apk">arm64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_armv7.apk">armv7</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_armv7.apk">armv7</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_ppc64le.apk">ppc64le</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_ppc64le.apk">ppc64le</a></td>
</tr> </tr>
<tr> <tr>
<th>Linux</th> <th>Linux</th>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_amd64">amd64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_amd64">amd64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_386">386</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_386">386</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_arm64">arm64</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_arm64">arm64</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_armv7">armv7</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_armv7">armv7</a></td>
<td><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_linux_ppc64le">ppc64le</a></td> <td><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_linux_ppc64le">ppc64le</a></td>
</tr> </tr>
<tr> <tr>
<th>macOS</th> <th>macOS</th>
<td colspan="4" style="text-align: center"><a href="https://github.com/satisfactorymodding/ficsit-cli/releases/latest/download/ficsit_darwin_all">darwin_all</a></td> <td colspan="4" style="text-align: center"><a href="https://github.com/Vilsol/ficsit-cli/releases/latest/download/ficsit_darwin_all">darwin_all</a></td>
<td>N/A</td> <td>N/A</td>
</tr> </tr>
</table> </table>
@ -76,38 +74,21 @@ A CLI tool for managing mods for the game [Satisfactory](https://www.satisfactor
To launch the interactive CLI, run the executable without any arguments. To launch the interactive CLI, run the executable without any arguments.
All screens display control hints at the bottom.
### Command Line ### Command Line
Run `ficsit help` to see a list of available commands and flags. Run `ficsit help` to see a list of available commands.
## Managing Installations
Unlike [Satisfactory Mod Manager](https://github.com/satisfactorymodding/SatisfactoryModManager/),
ficsit-cli does not automatically detect installations.
First, locate your game install path.
Check the [Modding FAQ](https://docs.ficsit.app/satisfactory-modding/latest/faq.html#Files_GameInstall)
to learn how to find it given your specific install situation.
To add installations in the interactive CLI, use `Installations` > `new installation`.
To add installations from the command line, use `ficsit-cli installation add yourPathHere`.
## Troubleshooting ## Troubleshooting
- Profile and installation records are located in `%APPDATA%\ficsit\` * Profile and installation records are located in `%APPDATA%\ficsit\`
- Downloads are cached in `%LOCALAPPDATA%\ficsit\downloadCache\` * Downloads are cached in `%LOCALAPPDATA%\ficsit\downloadCache\`
Get help on the [modding Discord](https://discord.ficsit.app/).
## Development ## Development
### Dependencies ### Dependencies
- [Go 1.21](https://go.dev/doc/install) * [Go 1.21](https://go.dev/doc/install)
- IDE of Choice. Goland or VSCode suggested. * IDE of Choice. Goland or VSCode suggested.
### Code Generation ### Code Generation

View file

@ -12,44 +12,43 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/mircearoata/pubgrub-go/pubgrub/semver"
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
const IconFilename = "Resources/Icon128.png" // This is the path UE expects for the icon const IconFilename = "Resources/Icon128.png" // This is the path UE expects for the icon
type Mod struct { type File struct {
ModReference string
Name string
Author string
Icon *string Icon *string
LatestVersion string ModReference string
Hash string
Plugin UPlugin
Size int64
} }
var loadedMods *xsync.MapOf[string, Mod] var loadedCache *xsync.MapOf[string, []File]
func GetCacheMods() (*xsync.MapOf[string, Mod], error) { func GetCache() (*xsync.MapOf[string, []File], error) {
if loadedMods != nil { if loadedCache != nil {
return loadedMods, nil return loadedCache, nil
} }
return LoadCacheMods() return LoadCache()
} }
func GetCacheMod(mod string) (Mod, error) { func GetCacheMod(mod string) ([]File, error) {
cache, err := GetCacheMods() cache, err := GetCache()
if err != nil { if err != nil {
return Mod{}, err return nil, err
} }
value, _ := cache.Load(mod) value, _ := cache.Load(mod)
return value, nil return value, nil
} }
func LoadCacheMods() (*xsync.MapOf[string, Mod], error) { func LoadCache() (*xsync.MapOf[string, []File], error) {
loadedMods = xsync.NewMapOf[string, Mod]() loadedCache = xsync.NewMapOf[string, []File]()
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
if _, err := os.Stat(downloadCache); os.IsNotExist(err) { if _, err := os.Stat(downloadCache); os.IsNotExist(err) {
return loadedMods, nil return loadedCache, nil
} }
items, err := os.ReadDir(downloadCache) items, err := os.ReadDir(downloadCache)
@ -61,45 +60,32 @@ func LoadCacheMods() (*xsync.MapOf[string, Mod], error) {
if item.IsDir() { if item.IsDir() {
continue continue
} }
if item.Name() == integrityFilename {
continue
}
_, err = addFileToCache(item.Name()) _, err = addFileToCache(item.Name())
if err != nil { if err != nil {
slog.Error("failed to add file to cache", slog.String("file", item.Name()), slog.Any("err", err)) slog.Error("failed to add file to cache", slog.String("file", item.Name()), slog.Any("err", err))
} }
} }
return loadedMods, nil return loadedCache, nil
} }
func addFileToCache(filename string) (*Mod, error) { func addFileToCache(filename string) (*File, error) {
cacheFile, err := readCacheFile(filename) cacheFile, err := readCacheFile(filename)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read cache file: %w", err) return nil, fmt.Errorf("failed to read cache file: %w", err)
} }
loadedMods.Compute(cacheFile.ModReference, func(oldValue Mod, loaded bool) (Mod, bool) { loadedCache.Compute(cacheFile.ModReference, func(oldValue []File, _ bool) ([]File, bool) {
if !loaded { return append(oldValue, *cacheFile), false
return *cacheFile, false
}
oldVersion, err := semver.NewVersion(oldValue.LatestVersion)
if err != nil {
slog.Error("failed to parse version", slog.String("version", oldValue.LatestVersion), slog.Any("err", err))
return *cacheFile, false
}
newVersion, err := semver.NewVersion(cacheFile.LatestVersion)
if err != nil {
slog.Error("failed to parse version", slog.String("version", cacheFile.LatestVersion), slog.Any("err", err))
return oldValue, false
}
if newVersion.Compare(oldVersion) > 0 {
return *cacheFile, false
}
return oldValue, false
}) })
return cacheFile, nil return cacheFile, nil
} }
func readCacheFile(filename string) (*Mod, error) { func readCacheFile(filename string) (*File, error) {
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
path := filepath.Join(downloadCache, filename) path := filepath.Join(downloadCache, filename)
stat, err := os.Stat(path) stat, err := os.Stat(path)
@ -146,6 +132,11 @@ func readCacheFile(filename string) (*Mod, error) {
modReference := strings.TrimSuffix(upluginFile.Name, ".uplugin") modReference := strings.TrimSuffix(upluginFile.Name, ".uplugin")
hash, err := getFileHash(filename)
if err != nil {
return nil, fmt.Errorf("failed to get file hash: %w", err)
}
var iconFile *zip.File var iconFile *zip.File
for _, file := range reader.File { for _, file := range reader.File {
if file.Name == IconFilename { if file.Name == IconFilename {
@ -169,11 +160,11 @@ func readCacheFile(filename string) (*Mod, error) {
icon = &iconData icon = &iconData
} }
return &Mod{ return &File{
ModReference: modReference, ModReference: modReference,
Name: uplugin.FriendlyName, Hash: hash,
Author: uplugin.CreatedBy, Size: size,
Icon: icon, Icon: icon,
LatestVersion: uplugin.SemVersion, Plugin: uplugin,
}, nil }, nil
} }

118
cli/cache/integrity.go vendored Normal file
View file

@ -0,0 +1,118 @@
package cache
import (
"encoding/json"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"time"
"github.com/puzpuzpuz/xsync/v3"
"github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/utils"
)
type hashInfo struct {
Modified time.Time
Hash string
Size int64
}
var hashCache *xsync.MapOf[string, hashInfo]
var integrityFilename = ".integrity"
func getFileHash(file string) (string, error) {
if hashCache == nil {
loadHashCache()
}
cachedHash, ok := hashCache.Load(file)
if !ok {
return cacheFileHash(file)
}
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
stat, err := os.Stat(filepath.Join(downloadCache, file))
if err != nil {
return "", fmt.Errorf("failed to stat file: %w", err)
}
if stat.Size() != cachedHash.Size || stat.ModTime() != cachedHash.Modified {
return cacheFileHash(file)
}
return cachedHash.Hash, nil
}
func cacheFileHash(file string) (string, error) {
downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache")
stat, err := os.Stat(filepath.Join(downloadCache, file))
if err != nil {
return "", fmt.Errorf("failed to stat file: %w", err)
}
f, err := os.Open(filepath.Join(downloadCache, file))
if err != nil {
return "", fmt.Errorf("failed to open file: %w", err)
}
defer f.Close()
hash, err := utils.SHA256Data(f)
if err != nil {
return "", fmt.Errorf("failed to hash file: %w", err)
}
hashCache.Store(file, hashInfo{
Hash: hash,
Size: stat.Size(),
Modified: stat.ModTime(),
})
saveHashCache()
return hash, nil
}
func loadHashCache() {
hashCache = xsync.NewMapOf[string, hashInfo]()
cacheFile := filepath.Join(viper.GetString("cache-dir"), "downloadCache", integrityFilename)
if _, err := os.Stat(cacheFile); os.IsNotExist(err) {
return
}
f, err := os.Open(cacheFile)
if err != nil {
slog.Warn("failed to open hash cache, recreating", slog.Any("err", err))
return
}
defer f.Close()
hashCacheJSON, err := io.ReadAll(f)
if err != nil {
slog.Warn("failed to read hash cache, recreating", slog.Any("err", err))
return
}
var plainCache map[string]hashInfo
if err := json.Unmarshal(hashCacheJSON, &plainCache); err != nil {
slog.Warn("failed to unmarshal hash cache, recreating", slog.Any("err", err))
return
}
for k, v := range plainCache {
hashCache.Store(k, v)
}
}
func saveHashCache() {
cacheFile := filepath.Join(viper.GetString("cache-dir"), "downloadCache", integrityFilename)
plainCache := make(map[string]hashInfo, hashCache.Size())
hashCache.Range(func(k string, v hashInfo) bool {
plainCache[k] = v
return true
})
hashCacheJSON, err := json.Marshal(plainCache)
if err != nil {
slog.Warn("failed to marshal hash cache", slog.Any("err", err))
return
}
if err := os.WriteFile(cacheFile, hashCacheJSON, 0o755); err != nil {
slog.Warn("failed to write hash cache", slog.Any("err", err))
return
}
}

View file

@ -5,7 +5,6 @@ type UPlugin struct {
FriendlyName string `json:"FriendlyName"` FriendlyName string `json:"FriendlyName"`
Description string `json:"Description"` Description string `json:"Description"`
CreatedBy string `json:"CreatedBy"` CreatedBy string `json:"CreatedBy"`
GameVersion string `json:"GameVersion"`
Plugins []Plugins `json:"Plugins"` Plugins []Plugins `json:"Plugins"`
} }
type Plugins struct { type Plugins struct {

View file

@ -8,7 +8,6 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/cli/cache" "github.com/satisfactorymodding/ficsit-cli/cli/cache"
"github.com/satisfactorymodding/ficsit-cli/cli/localregistry"
"github.com/satisfactorymodding/ficsit-cli/cli/provider" "github.com/satisfactorymodding/ficsit-cli/cli/provider"
"github.com/satisfactorymodding/ficsit-cli/ficsit" "github.com/satisfactorymodding/ficsit-cli/ficsit"
) )
@ -46,16 +45,11 @@ func InitCLI(apiOnly bool) (*GlobalContext, error) {
return nil, fmt.Errorf("failed to initialize installations: %w", err) return nil, fmt.Errorf("failed to initialize installations: %w", err)
} }
_, err = cache.LoadCacheMods() _, err = cache.LoadCache()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load cache: %w", err) return nil, fmt.Errorf("failed to load cache: %w", err)
} }
err = localregistry.Init()
if err != nil {
return nil, fmt.Errorf("failed to initialize local registry: %w", err)
}
globalContext = &GlobalContext{ globalContext = &GlobalContext{
Installations: installations, Installations: installations,
Profiles: profiles, Profiles: profiles,

View file

@ -3,15 +3,12 @@ package disk
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
"log/slog" "log/slog"
"net/textproto"
"net/url" "net/url"
"path" "path/filepath"
"slices"
"strings" "strings"
"time" "time"
@ -94,86 +91,7 @@ func testFTP(u *url.URL, options ...ftp.DialOption) (*ftp.ServerConn, bool, erro
return c, false, nil return c, false, nil
} }
func (l *ftpDisk) existsWithLock(res *puddle.Resource[*ftp.ServerConn], p string) (bool, error) { func (l *ftpDisk) Exists(path string) (bool, error) {
slog.Debug("checking if file exists", slog.String("path", clean(p)), slog.String("schema", "ftp"))
var protocolError *textproto.Error
_, err := res.Value().GetEntry(clean(p))
if err == nil {
return true, nil
}
if errors.As(err, &protocolError) {
switch protocolError.Code {
case ftp.StatusFileUnavailable:
return false, nil
case ftp.StatusNotImplemented:
// GetEntry uses MLST, which might not be supported by the server.
// Even though in this case the error is not coming from the server,
// the ftp library still returns it as a protocol error.
default:
// We won't handle any other kind of error, such as
// * temporary errors (4xx) - should be retried after a while, so we won't deal with the delay
// * connection errors (x2x) - can't really do anything about them
// * authentication errors (x3x) - can't do anything about them
return false, fmt.Errorf("failed to get path info: %w", err)
}
} else {
// This is a non-protocol error, so we can't be sure what it means.
return false, fmt.Errorf("failed to get path info: %w", err)
}
// In case MLST is not supported, we can try to LIST the target path.
// We can be sure that List() will actually execute LIST and not MLSD,
// since MLST was not supported in the previous step.
entries, err := res.Value().List(clean(p))
if err == nil {
if len(entries) > 0 {
// Some server implementations return an empty list for a nonexistent path,
// so we cannot be sure that no error means a directory exists unless it also contains some items.
// For files, when they exist, they will be listed as a single entry.
// TODO: so far the servers (just one) this was happening on also listed . and .. for valid dirs, because it was using `LIST -a`. Is that behaviour consistent that we can rely on it?
return true, nil
}
} else {
if errors.As(err, &protocolError) {
if protocolError.Code == ftp.StatusFileUnavailable {
return false, nil
}
}
// We won't handle any other kind of error, see above.
return false, fmt.Errorf("failed to list path: %w", err)
}
// If we got here, either the path is an empty directory,
// or it does not exist and the server is a weird implementation.
// List the parent directory to determine if the path exists
dir, err := l.readDirLock(res, path.Dir(clean(p)))
if err == nil {
found := false
for _, entry := range dir {
if entry.Name() == path.Base(clean(p)) {
found = true
break
}
}
return found, nil
}
if errors.As(err, &protocolError) {
if protocolError.Code == ftp.StatusFileUnavailable {
return false, nil
}
}
// We won't handle any other kind of error, see above.
return false, fmt.Errorf("failed to list parent path: %w", err)
}
func (l *ftpDisk) Exists(p string) (bool, error) {
res, err := l.acquire() res, err := l.acquire()
if err != nil { if err != nil {
return false, err return false, err
@ -181,7 +99,49 @@ func (l *ftpDisk) Exists(p string) (bool, error) {
defer res.Release() defer res.Release()
return l.existsWithLock(res, p) slog.Debug("checking if file exists", slog.String("path", clean(path)), slog.String("schema", "ftp"))
split := strings.Split(clean(path)[1:], "/")
for _, s := range split[:len(split)-1] {
dir, err := l.readDirLock(res, "")
if err != nil {
return false, err
}
currentDir, _ := res.Value().CurrentDir()
foundDir := false
for _, entry := range dir {
if entry.IsDir() && entry.Name() == s {
foundDir = true
break
}
}
if !foundDir {
return false, nil
}
slog.Debug("entering directory", slog.String("dir", s), slog.String("cwd", currentDir), slog.String("schema", "ftp"))
if err := res.Value().ChangeDir(s); err != nil {
return false, fmt.Errorf("failed to enter directory: %w", err)
}
}
dir, err := l.readDirLock(res, "")
if err != nil {
return false, fmt.Errorf("failed listing directory: %w", err)
}
found := false
for _, entry := range dir {
if entry.Name() == clean(filepath.Base(path)) {
found = true
break
}
}
return found, nil
} }
func (l *ftpDisk) Read(path string) ([]byte, error) { func (l *ftpDisk) Read(path string) ([]byte, error) {
@ -243,7 +203,7 @@ func (l *ftpDisk) Remove(path string) error {
return nil return nil
} }
func (l *ftpDisk) MkDir(p string) error { func (l *ftpDisk) MkDir(path string) error {
res, err := l.acquire() res, err := l.acquire()
if err != nil { if err != nil {
return err return err
@ -251,47 +211,34 @@ func (l *ftpDisk) MkDir(p string) error {
defer res.Release() defer res.Release()
lastExistingDir := clean(p) split := strings.Split(clean(path)[1:], "/")
for lastExistingDir != "/" && lastExistingDir != "." { for _, s := range split {
foundDir, err := l.existsWithLock(res, lastExistingDir) dir, err := l.readDirLock(res, "")
if err != nil { if err != nil {
return err return err
} }
if foundDir { currentDir, _ := res.Value().CurrentDir()
foundDir := false
for _, entry := range dir {
if entry.IsDir() && entry.Name() == s {
foundDir = true
break break
} }
lastExistingDir = path.Dir(lastExistingDir)
} }
remainingDirs := clean(p) if !foundDir {
slog.Debug("making directory", slog.String("dir", s), slog.String("cwd", currentDir), slog.String("schema", "ftp"))
if lastExistingDir != "/" && lastExistingDir != "." {
remainingDirs = strings.TrimPrefix(remainingDirs, lastExistingDir)
}
if len(remainingDirs) == 0 {
// Already exists
return nil
}
if err := res.Value().ChangeDir(lastExistingDir); err != nil {
return fmt.Errorf("failed to enter directory: %w", err)
}
split := strings.Split(clean(remainingDirs)[1:], "/")
for _, s := range split {
slog.Debug("making directory", slog.String("dir", s), slog.String("cwd", lastExistingDir), slog.String("schema", "ftp"))
if err := res.Value().MakeDir(s); err != nil { if err := res.Value().MakeDir(s); err != nil {
return fmt.Errorf("failed to make directory: %w", err) return fmt.Errorf("failed to make directory: %w", err)
} }
}
slog.Debug("entering directory", slog.String("dir", s), slog.String("cwd", lastExistingDir), slog.String("schema", "ftp")) slog.Debug("entering directory", slog.String("dir", s), slog.String("cwd", currentDir), slog.String("schema", "ftp"))
if err := res.Value().ChangeDir(s); err != nil { if err := res.Value().ChangeDir(s); err != nil {
return fmt.Errorf("failed to enter directory: %w", err) return fmt.Errorf("failed to enter directory: %w", err)
} }
lastExistingDir = path.Join(lastExistingDir, s)
} }
return nil return nil
@ -305,14 +252,7 @@ func (l *ftpDisk) ReadDir(path string) ([]Entry, error) {
defer res.Release() defer res.Release()
entries, err := l.readDirLock(res, path) return l.readDirLock(res, path)
if err != nil {
return nil, err
}
entries = slices.DeleteFunc(entries, func(i Entry) bool {
return i.Name() == "." || i.Name() == ".."
})
return entries, nil
} }
func (l *ftpDisk) readDirLock(res *puddle.Resource[*ftp.ServerConn], path string) ([]Entry, error) { func (l *ftpDisk) readDirLock(res *puddle.Resource[*ftp.ServerConn], path string) ([]Entry, error) {

View file

@ -183,8 +183,6 @@ func (i *Installations) DeleteInstallation(installPath string) error {
return nil return nil
} }
var rootExecutables = []string{"FactoryGame.exe", "FactoryServer.sh", "FactoryServer.exe", "FactoryGameSteam.exe", "FactoryGameEGS.exe"}
func (i *Installation) Validate(ctx *GlobalContext) error { func (i *Installation) Validate(ctx *GlobalContext) error {
found := false found := false
for _, p := range ctx.Profiles.Profiles { for _, p := range ctx.Profiles.Profiles {
@ -205,25 +203,31 @@ func (i *Installation) Validate(ctx *GlobalContext) error {
foundExecutable := false foundExecutable := false
var checkWait errgroup.Group exists, err := d.Exists(filepath.Join(i.BasePath(), "FactoryGame.exe"))
for _, executable := range rootExecutables {
e := executable
checkWait.Go(func() error {
exists, err := d.Exists(filepath.Join(i.BasePath(), e))
if !exists { if !exists {
if err != nil { if err != nil {
return fmt.Errorf("failed reading %s: %w", e, err) return fmt.Errorf("failed reading FactoryGame.exe: %w", err)
} }
} else { } else {
foundExecutable = true foundExecutable = true
} }
return nil
}) exists, err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.sh"))
if !exists {
if err != nil {
return fmt.Errorf("failed reading FactoryServer.sh: %w", err)
}
} else {
foundExecutable = true
} }
if err = checkWait.Wait(); err != nil { exists, err = d.Exists(filepath.Join(i.BasePath(), "FactoryServer.exe"))
return err //nolint:wrapcheck if !exists {
if err != nil {
return fmt.Errorf("failed reading FactoryServer.exe: %w", err)
}
} else {
foundExecutable = true
} }
if !foundExecutable { if !foundExecutable {
@ -239,18 +243,26 @@ var (
matchAllCap = regexp.MustCompile(`([a-z\d])([A-Z])`) matchAllCap = regexp.MustCompile(`([a-z\d])([A-Z])`)
) )
func (i *Installation) lockFilePath(ctx *GlobalContext, platform *Platform) string { func (i *Installation) LockFilePath(ctx *GlobalContext) (string, error) {
platform, err := i.GetPlatform(ctx)
if err != nil {
return "", err
}
lockFileName := ctx.Profiles.Profiles[i.Profile].Name lockFileName := ctx.Profiles.Profiles[i.Profile].Name
lockFileName = matchFirstCap.ReplaceAllString(lockFileName, "${1}_${2}") lockFileName = matchFirstCap.ReplaceAllString(lockFileName, "${1}_${2}")
lockFileName = matchAllCap.ReplaceAllString(lockFileName, "${1}_${2}") lockFileName = matchAllCap.ReplaceAllString(lockFileName, "${1}_${2}")
lockFileName = lockFileCleaner.ReplaceAllLiteralString(lockFileName, "-") lockFileName = lockFileCleaner.ReplaceAllLiteralString(lockFileName, "-")
lockFileName = strings.ToLower(lockFileName) + "-lock.json" lockFileName = strings.ToLower(lockFileName) + "-lock.json"
return filepath.Join(i.BasePath(), platform.LockfilePath, lockFileName) return filepath.Join(i.BasePath(), platform.LockfilePath, lockFileName), nil
} }
func (i *Installation) lockfile(ctx *GlobalContext, platform *Platform) (*resolver.LockFile, error) { func (i *Installation) LockFile(ctx *GlobalContext) (*resolver.LockFile, error) {
lockfilePath := i.lockFilePath(ctx, platform) lockfilePath, err := i.LockFilePath(ctx)
if err != nil {
return nil, err
}
d, err := i.GetDisk() d, err := i.GetDisk()
if err != nil { if err != nil {
@ -279,8 +291,11 @@ func (i *Installation) lockfile(ctx *GlobalContext, platform *Platform) (*resolv
return lockFile, nil return lockFile, nil
} }
func (i *Installation) writeLockFile(ctx *GlobalContext, platform *Platform, lockfile *resolver.LockFile) error { func (i *Installation) WriteLockFile(ctx *GlobalContext, lockfile *resolver.LockFile) error {
lockfilePath := i.lockFilePath(ctx, platform) lockfilePath, err := i.LockFilePath(ctx)
if err != nil {
return err
}
d, err := i.GetDisk() d, err := i.GetDisk()
if err != nil { if err != nil {
@ -326,15 +341,15 @@ func (i *Installation) Wipe() error {
return nil return nil
} }
func (i *Installation) resolveProfile(ctx *GlobalContext, platform *Platform) (*resolver.LockFile, error) { func (i *Installation) ResolveProfile(ctx *GlobalContext) (*resolver.LockFile, error) {
lockFile, err := i.lockfile(ctx, platform) lockFile, err := i.LockFile(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
depResolver := resolver.NewDependencyResolver(ctx.Provider) depResolver := resolver.NewDependencyResolver(ctx.Provider, viper.GetString("api-base"))
gameVersion, err := i.getGameVersion(platform) gameVersion, err := i.GetGameVersion(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to detect game version: %w", err) return nil, fmt.Errorf("failed to detect game version: %w", err)
} }
@ -344,37 +359,13 @@ func (i *Installation) resolveProfile(ctx *GlobalContext, platform *Platform) (*
return nil, fmt.Errorf("could not resolve mods: %w", err) return nil, fmt.Errorf("could not resolve mods: %w", err)
} }
if err := i.writeLockFile(ctx, platform, lockfile); err != nil { if err := i.WriteLockFile(ctx, lockfile); err != nil {
return nil, fmt.Errorf("failed to write lockfile: %w", err) return nil, fmt.Errorf("failed to write lockfile: %w", err)
} }
return lockfile, nil return lockfile, nil
} }
func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
platform, err := i.GetPlatform(ctx)
if err != nil {
return 0, err
}
return i.getGameVersion(platform)
}
func (i *Installation) LockFile(ctx *GlobalContext) (*resolver.LockFile, error) {
platform, err := i.GetPlatform(ctx)
if err != nil {
return nil, err
}
return i.lockfile(ctx, platform)
}
func (i *Installation) WriteLockFile(ctx *GlobalContext, lockfile *resolver.LockFile) error {
platform, err := i.GetPlatform(ctx)
if err != nil {
return err
}
return i.writeLockFile(ctx, platform, lockfile)
}
type InstallUpdateType string type InstallUpdateType string
var ( var (
@ -396,6 +387,10 @@ 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 {
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 fmt.Errorf("failed to detect platform: %w", err) return fmt.Errorf("failed to detect platform: %w", err)
@ -405,7 +400,7 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
if !i.Vanilla { if !i.Vanilla {
var err error var err error
lockfile, err = i.resolveProfile(ctx, platform) lockfile, err = i.ResolveProfile(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to resolve lockfile: %w", err) return fmt.Errorf("failed to resolve lockfile: %w", err)
} }
@ -426,41 +421,25 @@ func (i *Installation) Install(ctx *GlobalContext, updates chan<- InstallUpdate)
return fmt.Errorf("failed to read mods directory: %w", err) return fmt.Errorf("failed to read mods directory: %w", err)
} }
var deleteWait errgroup.Group
for _, entry := range dir { for _, entry := range dir {
if entry.IsDir() { if entry.IsDir() {
modName := entry.Name() if _, ok := lockfile.Mods[entry.Name()]; !ok {
mod, hasMod := lockfile.Mods[modName] modDir := filepath.Join(modsDirectory, entry.Name())
if hasMod {
_, hasTarget := mod.Targets[platform.TargetName]
hasMod = hasTarget
}
if !hasMod {
modName := entry.Name()
modDir := filepath.Join(modsDirectory, modName)
deleteWait.Go(func() error {
exists, err := d.Exists(filepath.Join(modDir, ".smm")) exists, err := d.Exists(filepath.Join(modDir, ".smm"))
if err != nil { if err != nil {
return err return err
} }
if exists { if exists {
slog.Info("deleting mod", slog.String("mod_reference", modName)) 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 fmt.Errorf("failed to delete mod directory: %w", err) return fmt.Errorf("failed to delete mod directory: %w", err)
} }
} }
return nil
})
} }
} }
} }
if err := deleteWait.Wait(); err != nil {
return fmt.Errorf("failed to remove old mods: %w", err)
}
slog.Info("starting installation", slog.Int("concurrency", viper.GetInt("concurrent-downloads")), slog.String("path", i.Path)) slog.Info("starting installation", slog.Int("concurrency", viper.GetInt("concurrent-downloads")), slog.String("path", i.Path))
errg := errgroup.Group{} errg := errgroup.Group{}
@ -499,10 +478,7 @@ 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 {
// The resolver validates that the resulting lockfile mods can be installed on the sides where they are required return fmt.Errorf("%s@%s not available for %s", modReference, version.Version, platform.TargetName)
// so if the mod is missing this target, it means it is not required on this target
slog.Info("skipping mod not available for target", slog.String("mod_reference", modReference), slog.String("version", version.Version), slog.String("target", platform.TargetName))
return nil
} }
// 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
@ -547,19 +523,18 @@ 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 {
platform, err := i.GetPlatform(ctx) if err := i.Validate(ctx); err != nil {
if err != nil { return fmt.Errorf("failed to validate installation: %w", err)
return err
} }
lockFile, err := i.lockfile(ctx, platform) lockFile, err := i.LockFile(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to read lock file: %w", err) return fmt.Errorf("failed to read lock file: %w", err)
} }
resolver := resolver.NewDependencyResolver(ctx.Provider) resolver := resolver.NewDependencyResolver(ctx.Provider, viper.GetString("api-base"))
gameVersion, err := i.getGameVersion(platform) gameVersion, err := i.GetGameVersion(ctx)
if err != nil { if err != nil {
return fmt.Errorf("failed to detect game version: %w", err) return fmt.Errorf("failed to detect game version: %w", err)
} }
@ -578,7 +553,7 @@ func (i *Installation) UpdateMods(ctx *GlobalContext, mods []string) error {
return fmt.Errorf("failed to resolve dependencies: %w", err) return fmt.Errorf("failed to resolve dependencies: %w", err)
} }
if err := i.writeLockFile(ctx, platform, newLockFile); err != nil { if err := i.WriteLockFile(ctx, newLockFile); err != nil {
return fmt.Errorf("failed to write lock file: %w", err) return fmt.Errorf("failed to write lock file: %w", err)
} }
@ -692,13 +667,30 @@ type gameVersionFile struct {
IsPromotedBuild int `json:"IsPromotedBuild"` IsPromotedBuild int `json:"IsPromotedBuild"`
} }
func (i *Installation) getGameVersion(platform *Platform) (int, error) { func (i *Installation) GetGameVersion(ctx *GlobalContext) (int, error) {
if err := i.Validate(ctx); err != nil {
return 0, fmt.Errorf("failed to validate installation: %w", err)
}
platform, err := i.GetPlatform(ctx)
if err != nil {
return 0, err
}
d, err := i.GetDisk() d, err := i.GetDisk()
if err != nil { if err != nil {
return 0, err return 0, err
} }
fullPath := filepath.Join(i.BasePath(), platform.VersionPath) fullPath := filepath.Join(i.BasePath(), platform.VersionPath)
exists, err := d.Exists(fullPath)
if err != nil {
return 0, err
}
if !exists {
return 0, errors.New("game version file does not exist")
}
file, err := d.Read(fullPath) file, err := d.Read(fullPath)
if err != nil { if err != nil {

View file

@ -1,106 +0,0 @@
package localregistry
import (
"database/sql"
"fmt"
)
var migrations = []func(*sql.Tx) error{
initialSetup,
addRequiredOnRemote,
}
func applyMigrations(db *sql.DB) error {
// user_version will store the 1-indexed migration that was last applied
var nextMigration int
err := db.QueryRow("PRAGMA user_version;").Scan(&nextMigration)
if err != nil {
return fmt.Errorf("failed to get user_version: %w", err)
}
for i := nextMigration; i < len(migrations); i++ {
err := applyMigration(db, i)
if err != nil {
return fmt.Errorf("failed to apply migration %d: %w", i, err)
}
}
return nil
}
func applyMigration(db *sql.DB, migrationIndex int) error {
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("failed to start transaction: %w", err)
}
// Will noop if the transaction was committed
defer tx.Rollback() //nolint:errcheck
err = migrations[migrationIndex](tx)
if err != nil {
return err
}
_, err = tx.Exec(fmt.Sprintf("PRAGMA user_version = %d;", migrationIndex+1))
if err != nil {
return fmt.Errorf("failed to set user_version: %w", err)
}
err = tx.Commit()
if err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}
return nil
}
func initialSetup(tx *sql.Tx) error {
// Create the initial user
_, err := tx.Exec(`
CREATE TABLE IF NOT EXISTS "versions" (
"id" TEXT NOT NULL PRIMARY KEY,
"mod_reference" TEXT NOT NULL,
"version" TEXT NOT NULL,
"game_version" TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS "mod_reference" ON "versions" ("mod_reference");
CREATE UNIQUE INDEX IF NOT EXISTS "mod_version" ON "versions" ("mod_reference", "version");
CREATE TABLE IF NOT EXISTS "dependencies" (
"version_id" TEXT NOT NULL,
"dependency" TEXT NOT NULL,
"condition" TEXT NOT NULL,
"optional" INT NOT NULL,
FOREIGN KEY ("version_id") REFERENCES "versions" ("id") ON DELETE CASCADE,
PRIMARY KEY ("version_id", "dependency")
);
CREATE TABLE IF NOT EXISTS "targets" (
"version_id" TEXT NOT NULL,
"target_name" TEXT NOT NULL,
"link" TEXT NOT NULL,
"hash" TEXT NOT NULL,
"size" INT NOT NULL,
FOREIGN KEY ("version_id") REFERENCES "versions" ("id") ON DELETE CASCADE,
PRIMARY KEY ("version_id", "target_name")
);
`)
if err != nil {
return fmt.Errorf("failed to create initial tables: %w", err)
}
return nil
}
func addRequiredOnRemote(tx *sql.Tx) error {
_, err := tx.Exec(`
ALTER TABLE "versions" ADD COLUMN "required_on_remote" INT NOT NULL DEFAULT 1;
`)
if err != nil {
return fmt.Errorf("failed to add required_on_remote column: %w", err)
}
return nil
}

View file

@ -1,175 +0,0 @@
package localregistry
import (
"database/sql"
"fmt"
"log/slog"
"os"
"path/filepath"
"sync"
"github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/ficsit"
// sqlite driver
_ "modernc.org/sqlite"
)
var db *sql.DB
var dbWriteMutex = sync.Mutex{}
func Init() error {
dbPath := filepath.Join(viper.GetString("cache-dir"), "registry.db")
err := os.MkdirAll(filepath.Dir(dbPath), 0o777)
if err != nil {
return fmt.Errorf("failed to create local registry directory: %w", err)
}
db, err = sql.Open("sqlite", dbPath)
if err != nil {
return fmt.Errorf("failed to open database: %w", err)
}
// Set pragmas here because modernc.org/sqlite does not support them in the connection string
_, err = db.Exec(`
PRAGMA journal_mode = WAL;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
`)
if err != nil {
return fmt.Errorf("failed to setup connection pragmas: %w", err)
}
err = applyMigrations(db)
if err != nil {
return fmt.Errorf("failed to apply migrations: %w", err)
}
return nil
}
func Add(modReference string, modVersions []ficsit.ModVersion) {
dbWriteMutex.Lock()
defer dbWriteMutex.Unlock()
tx, err := db.Begin()
if err != nil {
slog.Error("failed to start local registry transaction", slog.Any("err", err))
return
}
// In case the transaction is not committed, revert and release
defer tx.Rollback() //nolint:errcheck
_, err = tx.Exec("DELETE FROM versions WHERE mod_reference = ?", modReference)
if err != nil {
slog.Error("failed to delete existing mod versions from local registry", slog.Any("err", err))
return
}
for _, modVersion := range modVersions {
l := slog.With(slog.String("mod", modReference), slog.String("version", modVersion.Version))
_, err = tx.Exec("INSERT INTO versions (id, mod_reference, version, game_version, required_on_remote) VALUES (?, ?, ?, ?, ?)", modVersion.ID, modReference, modVersion.Version, modVersion.GameVersion, modVersion.RequiredOnRemote)
if err != nil {
l.Error("failed to insert mod version into local registry", slog.Any("err", err))
return
}
for _, dependency := range modVersion.Dependencies {
_, err = tx.Exec("INSERT INTO dependencies (version_id, dependency, condition, optional) VALUES (?, ?, ?, ?)", modVersion.ID, dependency.ModID, dependency.Condition, dependency.Optional)
if err != nil {
l.Error("failed to insert dependency into local registry", slog.String("dependency", dependency.ModID), slog.Any("err", err))
return
}
}
for _, target := range modVersion.Targets {
_, err = tx.Exec("INSERT INTO targets (version_id, target_name, link, hash, size) VALUES (?, ?, ?, ?, ?)", modVersion.ID, target.TargetName, target.Link, target.Hash, target.Size)
if err != nil {
l.Error("failed to insert target into local registry", slog.Any("target", target.TargetName), slog.Any("err", err))
return
}
}
}
err = tx.Commit()
if err != nil {
slog.Error("failed to commit local registry transaction", slog.Any("err", err))
return
}
}
func GetModVersions(modReference string) ([]ficsit.ModVersion, error) {
versionRows, err := db.Query("SELECT id, version, game_version, required_on_remote FROM versions WHERE mod_reference = ?", modReference)
if err != nil {
return nil, fmt.Errorf("failed to fetch mod versions from local registry: %w", err)
}
defer versionRows.Close()
var versions []ficsit.ModVersion
for versionRows.Next() {
var version ficsit.ModVersion
err = versionRows.Scan(&version.ID, &version.Version, &version.GameVersion, &version.RequiredOnRemote)
if err != nil {
return nil, fmt.Errorf("failed to scan version row: %w", err)
}
dependencies, err := getVersionDependencies(version.ID)
if err != nil {
return nil, err
}
version.Dependencies = dependencies
targets, err := getVersionTargets(version.ID)
if err != nil {
return nil, err
}
version.Targets = targets
versions = append(versions, version)
}
return versions, nil
}
func getVersionDependencies(versionID string) ([]ficsit.Dependency, error) {
var dependencies []ficsit.Dependency
dependencyRows, err := db.Query("SELECT dependency, condition, optional FROM dependencies WHERE version_id = ?", versionID)
if err != nil {
return nil, fmt.Errorf("failed to fetch dependencies from local registry: %w", err)
}
defer dependencyRows.Close()
for dependencyRows.Next() {
var dependency ficsit.Dependency
err = dependencyRows.Scan(&dependency.ModID, &dependency.Condition, &dependency.Optional)
if err != nil {
return nil, fmt.Errorf("failed to scan dependency row: %w", err)
}
dependencies = append(dependencies, dependency)
}
return dependencies, nil
}
func getVersionTargets(versionID string) ([]ficsit.Target, error) {
var targets []ficsit.Target
targetRows, err := db.Query("SELECT target_name, link, hash, size FROM targets WHERE version_id = ?", versionID)
if err != nil {
return nil, fmt.Errorf("failed to fetch targets from local registry: %w", err)
}
defer targetRows.Close()
for targetRows.Next() {
var target ficsit.Target
err = targetRows.Scan(&target.TargetName, &target.Link, &target.Hash, &target.Size)
if err != nil {
return nil, fmt.Errorf("failed to scan target row: %w", err)
}
targets = append(targets, target)
}
return targets, nil
}

View file

@ -35,15 +35,4 @@ var platforms = []Platform{
LockfilePath: filepath.Join("FactoryGame", "Mods"), LockfilePath: filepath.Join("FactoryGame", "Mods"),
TargetName: "WindowsServer", TargetName: "WindowsServer",
}, },
// 1.0 stuff
{
VersionPath: filepath.Join("Engine", "Binaries", "Win64", "FactoryGameSteam-Win64-Shipping.version"),
LockfilePath: filepath.Join("FactoryGame", "Mods"),
TargetName: "Windows",
},
{
VersionPath: filepath.Join("Engine", "Binaries", "Win64", "FactoryGameEGS-Win64-Shipping.version"),
LockfilePath: filepath.Join("FactoryGame", "Mods"),
TargetName: "Windows",
},
} }

View file

@ -1,6 +1,7 @@
package cli package cli
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -298,7 +299,7 @@ func (p *Profile) Resolve(resolver resolver.DependencyResolver, lockFile *resolv
} }
} }
resultLockfile, err := resolver.ResolveModDependencies(toResolve, lockFile, gameVersion, p.RequiredTargets) resultLockfile, err := resolver.ResolveModDependencies(context.TODO(), toResolve, lockFile, gameVersion, p.RequiredTargets)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed resolving profile dependencies: %w", err) return nil, fmt.Errorf("failed resolving profile dependencies: %w", err)
} }

View file

@ -1,41 +0,0 @@
package provider
import (
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/ficsit"
)
func convertFicsitVersionsToResolver(versions []ficsit.ModVersion) []resolver.ModVersion {
modVersions := make([]resolver.ModVersion, len(versions))
for i, modVersion := range versions {
dependencies := make([]resolver.Dependency, len(modVersion.Dependencies))
for j, dependency := range modVersion.Dependencies {
dependencies[j] = resolver.Dependency{
ModID: dependency.ModID,
Condition: dependency.Condition,
Optional: dependency.Optional,
}
}
targets := make([]resolver.Target, len(modVersion.Targets))
for j, target := range modVersion.Targets {
targets[j] = resolver.Target{
TargetName: resolver.TargetName(target.TargetName),
Link: viper.GetString("api-base") + target.Link,
Hash: target.Hash,
Size: target.Size,
}
}
modVersions[i] = resolver.ModVersion{
Version: modVersion.Version,
GameVersion: modVersion.GameVersion,
Dependencies: dependencies,
Targets: targets,
RequiredOnRemote: modVersion.RequiredOnRemote,
}
}
return modVersions
}

View file

@ -7,7 +7,6 @@ import (
"github.com/Khan/genqlient/graphql" "github.com/Khan/genqlient/graphql"
resolver "github.com/satisfactorymodding/ficsit-resolver" resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/satisfactorymodding/ficsit-cli/cli/localregistry"
"github.com/satisfactorymodding/ficsit-cli/ficsit" "github.com/satisfactorymodding/ficsit-cli/ficsit"
) )
@ -29,6 +28,38 @@ func (p FicsitProvider) GetMod(context context.Context, modReference string) (*f
return ficsit.GetMod(context, p.client, modReference) return ficsit.GetMod(context, p.client, modReference)
} }
func (p FicsitProvider) ModVersions(context context.Context, modReference string, filter ficsit.VersionFilter) (*ficsit.ModVersionsResponse, error) {
return ficsit.ModVersions(context, p.client, modReference, filter)
}
func (p FicsitProvider) SMLVersions(context context.Context) ([]resolver.SMLVersion, error) {
response, err := ficsit.SMLVersions(context, p.client)
if err != nil {
return nil, err
}
smlVersions := make([]resolver.SMLVersion, len(response.SmlVersions.Sml_versions))
for i, version := range response.GetSmlVersions().Sml_versions {
targets := make([]resolver.SMLVersionTarget, len(version.Targets))
for j, target := range version.Targets {
targets[j] = resolver.SMLVersionTarget{
TargetName: resolver.TargetName(target.TargetName),
Link: target.Link,
}
}
smlVersions[i] = resolver.SMLVersion{
ID: version.Id,
Version: version.Version,
SatisfactoryVersion: version.Satisfactory_version,
Targets: targets,
}
}
return smlVersions, nil
}
func (p FicsitProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) { func (p FicsitProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) {
response, err := ficsit.GetAllModVersions(modID) response, err := ficsit.GetAllModVersions(modID)
if err != nil { if err != nil {
@ -39,9 +70,36 @@ func (p FicsitProvider) ModVersionsWithDependencies(_ context.Context, modID str
return nil, errors.New(response.Error.Message) return nil, errors.New(response.Error.Message)
} }
localregistry.Add(modID, response.Data) modVersions := make([]resolver.ModVersion, len(response.Data))
for i, modVersion := range response.Data {
dependencies := make([]resolver.Dependency, len(modVersion.Dependencies))
for j, dependency := range modVersion.Dependencies {
dependencies[j] = resolver.Dependency{
ModID: dependency.ModID,
Condition: dependency.Condition,
Optional: dependency.Optional,
}
}
return convertFicsitVersionsToResolver(response.Data), nil targets := make([]resolver.Target, len(modVersion.Targets))
for j, target := range modVersion.Targets {
targets[j] = resolver.Target{
VersionID: target.VersionID,
TargetName: resolver.TargetName(target.TargetName),
Hash: target.Hash,
Size: target.Size,
}
}
modVersions[i] = resolver.ModVersion{
ID: modVersion.ID,
Version: modVersion.Version,
Dependencies: dependencies,
Targets: targets,
}
}
return modVersions, err
} }
func (p FicsitProvider) GetModName(context context.Context, modReference string) (*resolver.ModName, error) { func (p FicsitProvider) GetModName(context context.Context, modReference string) (*resolver.ModName, error) {

View file

@ -2,6 +2,7 @@ package provider
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@ -9,7 +10,6 @@ import (
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"
"github.com/satisfactorymodding/ficsit-cli/cli/localregistry"
"github.com/satisfactorymodding/ficsit-cli/ficsit" "github.com/satisfactorymodding/ficsit-cli/ficsit"
) )
@ -20,14 +20,18 @@ 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.GetCacheMods() cachedMods, err := cache.GetCache()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get cache: %w", err) return nil, fmt.Errorf("failed to get cache: %w", err)
} }
mods := make([]ficsit.ModsModsGetModsModsMod, 0) mods := make([]ficsit.ModsModsGetModsModsMod, 0)
cachedMods.Range(func(modReference string, cachedMod cache.Mod) bool { cachedMods.Range(func(modReference string, files []cache.File) bool {
if modReference == "SML" {
return true
}
if len(filter.References) > 0 { if len(filter.References) > 0 {
skip := true skip := true
@ -45,7 +49,7 @@ func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit
mods = append(mods, ficsit.ModsModsGetModsModsMod{ mods = append(mods, ficsit.ModsModsGetModsModsMod{
Id: modReference, Id: modReference,
Name: cachedMod.Name, Name: files[0].Plugin.FriendlyName,
Mod_reference: modReference, Mod_reference: modReference,
Last_version_date: time.Now(), Last_version_date: time.Now(),
Created_at: time.Now(), Created_at: time.Now(),
@ -88,14 +92,18 @@ 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) {
cachedMod, err := cache.GetCacheMod(modReference) cachedModFiles, err := cache.GetCacheMod(modReference)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get cache: %w", err) return nil, fmt.Errorf("failed to get cache: %w", err)
} }
if len(cachedModFiles) == 0 {
return nil, errors.New("mod not found")
}
authors := make([]ficsit.GetModModAuthorsUserMod, 0) authors := make([]ficsit.GetModModAuthorsUserMod, 0)
for _, author := range strings.Split(cachedMod.Author, ",") { for _, author := range strings.Split(cachedModFiles[0].Plugin.CreatedBy, ",") {
authors = append(authors, ficsit.GetModModAuthorsUserMod{ authors = append(authors, ficsit.GetModModAuthorsUserMod{
Role: "Unknown", Role: "Unknown",
User: ficsit.GetModModAuthorsUserModUser{ User: ficsit.GetModModAuthorsUserModUser{
@ -107,7 +115,7 @@ func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.G
return &ficsit.GetModResponse{ return &ficsit.GetModResponse{
Mod: ficsit.GetModMod{ Mod: ficsit.GetModMod{
Id: modReference, Id: modReference,
Name: cachedMod.Name, Name: cachedModFiles[0].Plugin.FriendlyName,
Mod_reference: modReference, Mod_reference: modReference,
Created_at: time.Now(), Created_at: time.Now(),
Views: 0, Views: 0,
@ -119,26 +127,56 @@ func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.G
}, nil }, nil
} }
func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) { func (p LocalProvider) SMLVersions(_ context.Context) ([]resolver.SMLVersion, error) {
modVersions, err := localregistry.GetModVersions(modID) cachedSMLFiles, err := cache.GetCacheMod("SML")
if err != nil {
return nil, fmt.Errorf("failed to get local mod versions: %w", err)
}
// TODO: only list as available the versions that have at least one target cached
return convertFicsitVersionsToResolver(modVersions), nil
}
func (p LocalProvider) GetModName(_ context.Context, modReference string) (*resolver.ModName, error) {
cachedMod, err := cache.GetCacheMod(modReference)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get cache: %w", err) return nil, fmt.Errorf("failed to get cache: %w", err)
} }
smlVersions := make([]resolver.SMLVersion, 0)
for _, smlFile := range cachedSMLFiles {
smlVersions = append(smlVersions, resolver.SMLVersion{
ID: "SML:" + smlFile.Plugin.SemVersion,
Version: smlFile.Plugin.SemVersion,
SatisfactoryVersion: 0, // TODO: where can this be obtained from?
})
}
return smlVersions, nil
}
func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) {
cachedModFiles, err := cache.GetCacheMod(modID)
if err != nil {
return nil, fmt.Errorf("failed to get cache: %w", err)
}
versions := make([]resolver.ModVersion, 0)
for _, modFile := range cachedModFiles {
versions = append(versions, resolver.ModVersion{
ID: modID + ":" + modFile.Plugin.SemVersion,
Version: modFile.Plugin.SemVersion,
})
}
return versions, nil
}
func (p LocalProvider) GetModName(_ context.Context, modReference string) (*resolver.ModName, error) {
cachedModFiles, err := cache.GetCacheMod(modReference)
if err != nil {
return nil, fmt.Errorf("failed to get cache: %w", err)
}
if len(cachedModFiles) == 0 {
return nil, errors.New("mod not found")
}
return &resolver.ModName{ return &resolver.ModName{
ID: modReference, ID: modReference,
Name: cachedMod.Name, Name: cachedModFiles[0].Plugin.FriendlyName,
ModReference: modReference, ModReference: modReference,
}, nil }, nil
} }

View file

@ -36,6 +36,13 @@ func (p MixedProvider) GetMod(context context.Context, modReference string) (*fi
return p.onlineProvider.GetMod(context, modReference) return p.onlineProvider.GetMod(context, modReference)
} }
func (p MixedProvider) SMLVersions(context context.Context) ([]resolver.SMLVersion, error) {
if p.Offline {
return p.offlineProvider.SMLVersions(context) // nolint
}
return p.onlineProvider.SMLVersions(context) // nolint
}
func (p MixedProvider) ModVersionsWithDependencies(context context.Context, modID string) ([]resolver.ModVersion, error) { func (p MixedProvider) ModVersionsWithDependencies(context context.Context, modID string) ([]resolver.ModVersion, error) {
if p.Offline { if p.Offline {
return p.offlineProvider.ModVersionsWithDependencies(context, modID) // nolint return p.offlineProvider.ModVersionsWithDependencies(context, modID) // nolint

View file

@ -1,15 +1,15 @@
package cli package cli
import ( import (
"context"
"log/slog" "log/slog"
"math" "math"
"os" "os"
"path/filepath"
"testing" "testing"
"time"
"github.com/MarvinJWendt/testza" "github.com/MarvinJWendt/testza"
resolver "github.com/satisfactorymodding/ficsit-resolver" resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/cfg" "github.com/satisfactorymodding/ficsit-cli/cfg"
) )
@ -34,108 +34,6 @@ func installWatcher() chan<- InstallUpdate {
return c return c
} }
func TestClientOnlyMod(t *testing.T) {
ctx, err := InitCLI(false)
testza.AssertNoError(t, err)
err = ctx.Wipe()
testza.AssertNoError(t, err)
ctx.Provider = MockProvider{}
profileName := "ClientOnlyModTest"
profile, err := ctx.Profiles.AddProfile(profileName)
profile.RequiredTargets = []resolver.TargetName{resolver.TargetNameWindows, resolver.TargetNameWindowsServer, resolver.TargetNameLinuxServer}
testza.AssertNoError(t, err)
testza.AssertNoError(t, profile.AddMod("ClientOnlyMod", "<=0.0.1"))
serverLocation := os.Getenv("SF_DEDICATED_SERVER")
if serverLocation != "" {
time.Sleep(time.Second)
testza.AssertNoError(t, os.RemoveAll(filepath.Join(serverLocation, "FactoryGame", "Mods")))
time.Sleep(time.Second)
installation, err := ctx.Installations.AddInstallation(ctx, serverLocation, profileName)
testza.AssertNoError(t, err)
testza.AssertNotNil(t, installation)
err = installation.Install(ctx, installWatcher())
testza.AssertNoError(t, err)
}
}
func TestServerOnlyMod(t *testing.T) {
ctx, err := InitCLI(false)
testza.AssertNoError(t, err)
err = ctx.Wipe()
testza.AssertNoError(t, err)
ctx.Provider = MockProvider{}
profileName := "ServerOnlyModTest"
profile, err := ctx.Profiles.AddProfile(profileName)
profile.RequiredTargets = []resolver.TargetName{resolver.TargetNameWindows, resolver.TargetNameWindowsServer, resolver.TargetNameLinuxServer}
testza.AssertNoError(t, err)
testza.AssertNoError(t, profile.AddMod("ServerOnlyMod", "<=0.0.1"))
serverLocation := os.Getenv("SF_DEDICATED_SERVER")
if serverLocation != "" {
time.Sleep(time.Second)
testza.AssertNoError(t, os.RemoveAll(filepath.Join(serverLocation, "FactoryGame", "Mods")))
time.Sleep(time.Second)
installation, err := ctx.Installations.AddInstallation(ctx, serverLocation, profileName)
testza.AssertNoError(t, err)
testza.AssertNotNil(t, installation)
err = installation.Install(ctx, installWatcher())
testza.AssertNoError(t, err)
}
}
func TestRemoveWhenNotSupported(t *testing.T) {
ctx, err := InitCLI(false)
testza.AssertNoError(t, err)
err = ctx.Wipe()
testza.AssertNoError(t, err)
ctx.Provider = MockProvider{}
profileName := "ClientOnlyModTest"
profile, err := ctx.Profiles.AddProfile(profileName)
profile.RequiredTargets = []resolver.TargetName{resolver.TargetNameWindows, resolver.TargetNameWindowsServer, resolver.TargetNameLinuxServer}
testza.AssertNoError(t, err)
testza.AssertNoError(t, profile.AddMod("LaterClientOnlyMod", "0.0.1"))
serverLocation := os.Getenv("SF_DEDICATED_SERVER")
if serverLocation != "" {
time.Sleep(time.Second)
testza.AssertNoError(t, os.RemoveAll(filepath.Join(serverLocation, "FactoryGame", "Mods")))
time.Sleep(time.Second)
installation, err := ctx.Installations.AddInstallation(ctx, serverLocation, profileName)
testza.AssertNoError(t, err)
testza.AssertNotNil(t, installation)
err = installation.Install(ctx, installWatcher())
testza.AssertNoError(t, err)
_, err = os.Stat(filepath.Join(serverLocation, "FactoryGame", "Mods", "LaterClientOnlyMod"))
testza.AssertNoError(t, err)
testza.AssertNoError(t, profile.AddMod("LaterClientOnlyMod", "0.0.2"))
err = installation.Install(ctx, installWatcher())
testza.AssertNoError(t, err)
_, err = os.Stat(filepath.Join(serverLocation, "FactoryGame", "Mods", "LaterClientOnlyMod"))
testza.AssertNotNil(t, err)
testza.AssertErrorIs(t, err, os.ErrNotExist)
}
}
func TestUpdateMods(t *testing.T) { func TestUpdateMods(t *testing.T) {
ctx, err := InitCLI(false) ctx, err := InitCLI(false)
testza.AssertNoError(t, err) testza.AssertNoError(t, err)
@ -145,9 +43,9 @@ func TestUpdateMods(t *testing.T) {
ctx.Provider = MockProvider{} ctx.Provider = MockProvider{}
depResolver := resolver.NewDependencyResolver(ctx.Provider) depResolver := resolver.NewDependencyResolver(ctx.Provider, viper.GetString("api-base"))
oldLockfile, err := depResolver.ResolveModDependencies(map[string]string{ oldLockfile, err := depResolver.ResolveModDependencies(context.Background(), map[string]string{
"FicsitRemoteMonitoring": "0.9.8", "FicsitRemoteMonitoring": "0.9.8",
}, nil, math.MaxInt, nil) }, nil, math.MaxInt, nil)

View file

@ -70,28 +70,19 @@ func (m MockProvider) Mods(_ context.Context, f ficsit.ModFilter) (*ficsit.ModsR
}, nil }, nil
} }
var windowsTarget = resolver.Target{
TargetName: "Windows",
Link: "https://api.ficsit.dev/v1/version/7QcfNdo5QAAyoC/Windows/download",
Hash: "698df20278b3de3ec30405569a22050c6721cc682389312258c14948bd8f38ae",
}
var windowsServerTarget = resolver.Target{
TargetName: "WindowsServer",
Link: "https://api.ficsit.dev/v1/version/7QcfNdo5QAAyoC/WindowsServer/download",
Hash: "7be01ed372e0cf3287a04f5cb32bb9dcf6f6e7a5b7603b7e43669ec4c6c1457f",
}
var linuxServerTarget = resolver.Target{
TargetName: "LinuxServer",
Link: "https://api.ficsit.dev/v1/version/7QcfNdo5QAAyoC/LinuxServer/download",
Hash: "bdbd4cb1b472a5316621939ae2fe270fd0e3c0f0a75666a9cbe74ff1313c3663",
}
var commonTargets = []resolver.Target{ var commonTargets = []resolver.Target{
windowsTarget, {
windowsServerTarget, TargetName: "Windows",
linuxServerTarget, Hash: "698df20278b3de3ec30405569a22050c6721cc682389312258c14948bd8f38ae",
},
{
TargetName: "WindowsServer",
Hash: "7be01ed372e0cf3287a04f5cb32bb9dcf6f6e7a5b7603b7e43669ec4c6c1457f",
},
{
TargetName: "LinuxServer",
Hash: "bdbd4cb1b472a5316621939ae2fe270fd0e3c0f0a75666a9cbe74ff1313c3663",
},
} }
func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID string) ([]resolver.ModVersion, error) { func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID string) ([]resolver.ModVersion, error) {
@ -99,6 +90,7 @@ func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID str
case "AreaActions": case "AreaActions":
return []resolver.ModVersion{ return []resolver.ModVersion{
{ {
ID: "7QcfNdo5QAAyoC",
Version: "1.6.7", Version: "1.6.7",
Dependencies: []resolver.Dependency{ Dependencies: []resolver.Dependency{
{ {
@ -108,9 +100,9 @@ func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID str
}, },
}, },
Targets: commonTargets, Targets: commonTargets,
RequiredOnRemote: true,
}, },
{ {
ID: "7QcfNdo5QAAyoC",
Version: "1.6.6", Version: "1.6.6",
Dependencies: []resolver.Dependency{ Dependencies: []resolver.Dependency{
{ {
@ -120,9 +112,9 @@ func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID str
}, },
}, },
Targets: commonTargets, Targets: commonTargets,
RequiredOnRemote: true,
}, },
{ {
ID: "7QcfNdo5QAAyoC",
Version: "1.6.5", Version: "1.6.5",
Dependencies: []resolver.Dependency{ Dependencies: []resolver.Dependency{
{ {
@ -132,12 +124,12 @@ func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID str
}, },
}, },
Targets: commonTargets, Targets: commonTargets,
RequiredOnRemote: true,
}, },
}, nil }, nil
case "FicsitRemoteMonitoring": case "FicsitRemoteMonitoring":
return []resolver.ModVersion{ return []resolver.ModVersion{
{ {
ID: "7QcfNdo5QAAyoC",
Version: "0.10.1", Version: "0.10.1",
Dependencies: []resolver.Dependency{ Dependencies: []resolver.Dependency{
{ {
@ -147,9 +139,9 @@ func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID str
}, },
}, },
Targets: commonTargets, Targets: commonTargets,
RequiredOnRemote: true,
}, },
{ {
ID: "7QcfNdo5QAAyoC",
Version: "0.10.0", Version: "0.10.0",
Dependencies: []resolver.Dependency{ Dependencies: []resolver.Dependency{
{ {
@ -159,9 +151,9 @@ func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID str
}, },
}, },
Targets: commonTargets, Targets: commonTargets,
RequiredOnRemote: true,
}, },
{ {
ID: "7QcfNdo5QAAyoC",
Version: "0.9.8", Version: "0.9.8",
Dependencies: []resolver.Dependency{ Dependencies: []resolver.Dependency{
{ {
@ -171,71 +163,6 @@ func (m MockProvider) ModVersionsWithDependencies(ctx context.Context, modID str
}, },
}, },
Targets: commonTargets, Targets: commonTargets,
RequiredOnRemote: true,
},
}, nil
case "ClientOnlyMod":
return []resolver.ModVersion{
{
Version: "0.0.1",
Dependencies: []resolver.Dependency{
{
ModID: "SML",
Condition: "^3.6.0",
Optional: false,
},
},
Targets: []resolver.Target{
windowsTarget,
},
RequiredOnRemote: false,
},
}, nil
case "ServerOnlyMod":
return []resolver.ModVersion{
{
Version: "0.0.1",
Dependencies: []resolver.Dependency{
{
ModID: "SML",
Condition: "^3.6.0",
Optional: false,
},
},
Targets: []resolver.Target{
windowsServerTarget,
linuxServerTarget,
},
RequiredOnRemote: false,
},
}, nil
case "LaterClientOnlyMod":
return []resolver.ModVersion{
{
Version: "0.0.1",
Dependencies: []resolver.Dependency{
{
ModID: "SML",
Condition: "^3.6.0",
Optional: false,
},
},
Targets: commonTargets,
RequiredOnRemote: true,
},
{
Version: "0.0.2",
Dependencies: []resolver.Dependency{
{
ModID: "SML",
Condition: "^3.6.0",
Optional: false,
},
},
Targets: []resolver.Target{
windowsTarget,
},
RequiredOnRemote: false,
}, },
}, nil }, nil
} }

View file

@ -131,8 +131,6 @@ func init() {
baseLocalDir = os.Getenv("APPDATA") baseLocalDir = os.Getenv("APPDATA")
case "linux": case "linux":
baseLocalDir = filepath.Join(os.Getenv("HOME"), ".local", "share") baseLocalDir = filepath.Join(os.Getenv("HOME"), ".local", "share")
case "darwin":
baseLocalDir = filepath.Join(os.Getenv("HOME"), "Library", "Application Support")
default: default:
panic("unsupported platform: " + runtime.GOOS) panic("unsupported platform: " + runtime.GOOS)
} }

View file

@ -6,7 +6,6 @@
"language": "en", "language": "en",
// words - list of words to be always considered correct // words - list of words to be always considered correct
"words": [ "words": [
"armv",
"ficsit", "ficsit",
"gofumpt", "gofumpt",
"Goland", "Goland",
@ -21,5 +20,7 @@
// flagWords - list of words to be always considered incorrect // flagWords - list of words to be always considered incorrect
// This is useful for offensive words and common spelling errors. // This is useful for offensive words and common spelling errors.
// cSpell:disable (don't complain about the words we listed here) // cSpell:disable (don't complain about the words we listed here)
"flagWords": ["hte"] "flagWords": [
"hte"
]
} }

View file

@ -24,12 +24,12 @@ cli mod manager for satisfactory
### SEE ALSO ### SEE ALSO
- [ficsit apply](ficsit_apply.md) - Apply profiles to all installations * [ficsit apply](ficsit_apply.md) - Apply profiles to all installations
- [ficsit cli](ficsit_cli.md) - Start interactive CLI (default) * [ficsit cli](ficsit_cli.md) - Start interactive CLI (default)
- [ficsit installation](ficsit_installation.md) - Manage installations * [ficsit installation](ficsit_installation.md) - Manage installations
- [ficsit profile](ficsit_profile.md) - Manage profiles * [ficsit profile](ficsit_profile.md) - Manage profiles
- [ficsit search](ficsit_search.md) - Search mods * [ficsit search](ficsit_search.md) - Search mods
- [ficsit smr](ficsit_smr.md) - Manage mods on SMR * [ficsit smr](ficsit_smr.md) - Manage mods on SMR
- [ficsit version](ficsit_version.md) - Print current version information * [ficsit version](ficsit_version.md) - Print current version information
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit apply [installation] ... [flags]
### SEE ALSO ### SEE ALSO
- [ficsit](ficsit.md) - cli mod manager for satisfactory * [ficsit](ficsit.md) - cli mod manager for satisfactory
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit cli [flags]
### SEE ALSO ### SEE ALSO
- [ficsit](ficsit.md) - cli mod manager for satisfactory * [ficsit](ficsit.md) - cli mod manager for satisfactory
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -29,11 +29,11 @@ Manage installations
### SEE ALSO ### SEE ALSO
- [ficsit](ficsit.md) - cli mod manager for satisfactory * [ficsit](ficsit.md) - cli mod manager for satisfactory
- [ficsit installation add](ficsit_installation_add.md) - Add an installation * [ficsit installation add](ficsit_installation_add.md) - Add an installation
- [ficsit installation ls](ficsit_installation_ls.md) - List all installations * [ficsit installation ls](ficsit_installation_ls.md) - List all installations
- [ficsit installation remove](ficsit_installation_remove.md) - Remove an installation * [ficsit installation remove](ficsit_installation_remove.md) - Remove an installation
- [ficsit installation set-profile](ficsit_installation_set-profile.md) - Change the profile of an installation * [ficsit installation set-profile](ficsit_installation_set-profile.md) - Change the profile of an installation
- [ficsit installation set-vanilla](ficsit_installation_set-vanilla.md) - Set the installation to vanilla mode or not * [ficsit installation set-vanilla](ficsit_installation_set-vanilla.md) - Set the installation to vanilla mode or not
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit installation add <path> [profile] [flags]
### SEE ALSO ### SEE ALSO
- [ficsit installation](ficsit_installation.md) - Manage installations * [ficsit installation](ficsit_installation.md) - Manage installations
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit installation ls [flags]
### SEE ALSO ### SEE ALSO
- [ficsit installation](ficsit_installation.md) - Manage installations * [ficsit installation](ficsit_installation.md) - Manage installations
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit installation remove <path> [flags]
### SEE ALSO ### SEE ALSO
- [ficsit installation](ficsit_installation.md) - Manage installations * [ficsit installation](ficsit_installation.md) - Manage installations
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit installation set-profile <path> <profile> [flags]
### SEE ALSO ### SEE ALSO
- [ficsit installation](ficsit_installation.md) - Manage installations * [ficsit installation](ficsit_installation.md) - Manage installations
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -34,6 +34,6 @@ ficsit installation set-vanilla <path> [flags]
### SEE ALSO ### SEE ALSO
- [ficsit installation](ficsit_installation.md) - Manage installations * [ficsit installation](ficsit_installation.md) - Manage installations
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -29,11 +29,11 @@ Manage profiles
### SEE ALSO ### SEE ALSO
- [ficsit](ficsit.md) - cli mod manager for satisfactory * [ficsit](ficsit.md) - cli mod manager for satisfactory
- [ficsit profile delete](ficsit_profile_delete.md) - Delete a profile * [ficsit profile delete](ficsit_profile_delete.md) - Delete a profile
- [ficsit profile ls](ficsit_profile_ls.md) - List all profiles * [ficsit profile ls](ficsit_profile_ls.md) - List all profiles
- [ficsit profile mods](ficsit_profile_mods.md) - List all mods in a profile * [ficsit profile mods](ficsit_profile_mods.md) - List all mods in a profile
- [ficsit profile new](ficsit_profile_new.md) - Create a new profile * [ficsit profile new](ficsit_profile_new.md) - Create a new profile
- [ficsit profile rename](ficsit_profile_rename.md) - Rename a profile * [ficsit profile rename](ficsit_profile_rename.md) - Rename a profile
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit profile delete <name> [flags]
### SEE ALSO ### SEE ALSO
- [ficsit profile](ficsit_profile.md) - Manage profiles * [ficsit profile](ficsit_profile.md) - Manage profiles
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit profile ls [flags]
### SEE ALSO ### SEE ALSO
- [ficsit profile](ficsit_profile.md) - Manage profiles * [ficsit profile](ficsit_profile.md) - Manage profiles
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit profile mods <profile> [flags]
### SEE ALSO ### SEE ALSO
- [ficsit profile](ficsit_profile.md) - Manage profiles * [ficsit profile](ficsit_profile.md) - Manage profiles
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit profile new <name> [flags]
### SEE ALSO ### SEE ALSO
- [ficsit profile](ficsit_profile.md) - Manage profiles * [ficsit profile](ficsit_profile.md) - Manage profiles
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit profile rename <old> <name> [flags]
### SEE ALSO ### SEE ALSO
- [ficsit profile](ficsit_profile.md) - Manage profiles * [ficsit profile](ficsit_profile.md) - Manage profiles
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -38,6 +38,6 @@ ficsit search [query] [flags]
### SEE ALSO ### SEE ALSO
- [ficsit](ficsit.md) - cli mod manager for satisfactory * [ficsit](ficsit.md) - cli mod manager for satisfactory
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -29,7 +29,7 @@ Manage mods on SMR
### SEE ALSO ### SEE ALSO
- [ficsit](ficsit.md) - cli mod manager for satisfactory * [ficsit](ficsit.md) - cli mod manager for satisfactory
- [ficsit smr upload](ficsit_smr_upload.md) - Upload a new mod version * [ficsit smr upload](ficsit_smr_upload.md) - Upload a new mod version
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -35,6 +35,6 @@ ficsit smr upload [flags] <mod-id> <file> <changelog...>
### SEE ALSO ### SEE ALSO
- [ficsit smr](ficsit_smr.md) - Manage mods on SMR * [ficsit smr](ficsit_smr.md) - Manage mods on SMR
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

View file

@ -33,6 +33,6 @@ ficsit version [flags]
### SEE ALSO ### SEE ALSO
- [ficsit](ficsit.md) - cli mod manager for satisfactory * [ficsit](ficsit.md) - cli mod manager for satisfactory
###### Auto generated by spf13/cobra on 7-Dec-2023 ###### Auto generated by spf13/cobra on 7-Dec-2023

Binary file not shown.

View file

@ -17,6 +17,15 @@ func init() {
client = InitAPI() client = InitAPI()
} }
func TestModVersions(t *testing.T) {
response, err := ModVersions(context.Background(), client, "SmartFoundations", VersionFilter{})
testza.AssertNoError(t, err)
testza.AssertNotNil(t, response)
testza.AssertNotNil(t, response.Mod)
testza.AssertNotNil(t, response.Mod.Versions)
testza.AssertNotZero(t, len(response.Mod.Versions))
}
func TestMods(t *testing.T) { func TestMods(t *testing.T) {
response, err := Mods(context.Background(), client, ModFilter{}) response, err := Mods(context.Background(), client, ModFilter{})
testza.AssertNoError(t, err) testza.AssertNoError(t, err)

View file

@ -0,0 +1,13 @@
# @genqlient(omitempty: true)
query ModVersions (
$modId: String!,
$filter: VersionFilter
) {
mod: getModByIdOrReference(modIdOrReference: $modId) {
id
versions (filter: $filter) {
id
version
}
}
}

View file

@ -0,0 +1,24 @@
# @genqlient(omitempty: true)
query ModVersionsWithDependencies (
$modId: String!,
) {
mod: getModByIdOrReference(modIdOrReference: $modId) {
id
versions (filter: { limit: 100 }) {
id
version
link
hash
dependencies {
mod_id
condition
optional
}
targets {
targetName
link
hash
}
}
}
}

View file

@ -0,0 +1,15 @@
# @genqlient(omitempty: true)
query SMLVersions {
smlVersions: getSMLVersions(filter: {limit: 100}) {
count
sml_versions {
id
version
satisfactory_version
targets {
targetName
link
}
}
}
}

View file

@ -355,6 +355,136 @@ func (v *ModFilter) GetHidden() bool { return v.Hidden }
// GetTagIDs returns ModFilter.TagIDs, and is useful for accessing the field via an interface. // GetTagIDs returns ModFilter.TagIDs, and is useful for accessing the field via an interface.
func (v *ModFilter) GetTagIDs() []string { return v.TagIDs } func (v *ModFilter) GetTagIDs() []string { return v.TagIDs }
// ModVersionsMod includes the requested fields of the GraphQL type Mod.
type ModVersionsMod struct {
Id string `json:"id"`
Versions []ModVersionsModVersionsVersion `json:"versions"`
}
// GetId returns ModVersionsMod.Id, and is useful for accessing the field via an interface.
func (v *ModVersionsMod) GetId() string { return v.Id }
// GetVersions returns ModVersionsMod.Versions, and is useful for accessing the field via an interface.
func (v *ModVersionsMod) GetVersions() []ModVersionsModVersionsVersion { return v.Versions }
// ModVersionsModVersionsVersion includes the requested fields of the GraphQL type Version.
type ModVersionsModVersionsVersion struct {
Id string `json:"id"`
Version string `json:"version"`
}
// GetId returns ModVersionsModVersionsVersion.Id, and is useful for accessing the field via an interface.
func (v *ModVersionsModVersionsVersion) GetId() string { return v.Id }
// GetVersion returns ModVersionsModVersionsVersion.Version, and is useful for accessing the field via an interface.
func (v *ModVersionsModVersionsVersion) GetVersion() string { return v.Version }
// ModVersionsResponse is returned by ModVersions on success.
type ModVersionsResponse struct {
Mod ModVersionsMod `json:"mod"`
}
// GetMod returns ModVersionsResponse.Mod, and is useful for accessing the field via an interface.
func (v *ModVersionsResponse) GetMod() ModVersionsMod { return v.Mod }
// ModVersionsWithDependenciesMod includes the requested fields of the GraphQL type Mod.
type ModVersionsWithDependenciesMod struct {
Id string `json:"id"`
Versions []ModVersionsWithDependenciesModVersionsVersion `json:"versions"`
}
// GetId returns ModVersionsWithDependenciesMod.Id, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesMod) GetId() string { return v.Id }
// GetVersions returns ModVersionsWithDependenciesMod.Versions, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesMod) GetVersions() []ModVersionsWithDependenciesModVersionsVersion {
return v.Versions
}
// ModVersionsWithDependenciesModVersionsVersion includes the requested fields of the GraphQL type Version.
type ModVersionsWithDependenciesModVersionsVersion struct {
Id string `json:"id"`
Version string `json:"version"`
Link string `json:"link"`
Hash string `json:"hash"`
Dependencies []ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency `json:"dependencies"`
Targets []ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget `json:"targets"`
}
// GetId returns ModVersionsWithDependenciesModVersionsVersion.Id, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersion) GetId() string { return v.Id }
// GetVersion returns ModVersionsWithDependenciesModVersionsVersion.Version, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersion) GetVersion() string { return v.Version }
// GetLink returns ModVersionsWithDependenciesModVersionsVersion.Link, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersion) GetLink() string { return v.Link }
// GetHash returns ModVersionsWithDependenciesModVersionsVersion.Hash, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersion) GetHash() string { return v.Hash }
// GetDependencies returns ModVersionsWithDependenciesModVersionsVersion.Dependencies, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersion) GetDependencies() []ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency {
return v.Dependencies
}
// GetTargets returns ModVersionsWithDependenciesModVersionsVersion.Targets, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersion) GetTargets() []ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget {
return v.Targets
}
// ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency includes the requested fields of the GraphQL type VersionDependency.
type ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency struct {
Mod_id string `json:"mod_id"`
Condition string `json:"condition"`
Optional bool `json:"optional"`
}
// GetMod_id returns ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency.Mod_id, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency) GetMod_id() string {
return v.Mod_id
}
// GetCondition returns ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency.Condition, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency) GetCondition() string {
return v.Condition
}
// GetOptional returns ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency.Optional, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency) GetOptional() bool {
return v.Optional
}
// ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget includes the requested fields of the GraphQL type VersionTarget.
type ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget struct {
TargetName TargetName `json:"targetName"`
Link string `json:"link"`
Hash string `json:"hash"`
}
// GetTargetName returns ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget.TargetName, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget) GetTargetName() TargetName {
return v.TargetName
}
// GetLink returns ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget.Link, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget) GetLink() string {
return v.Link
}
// GetHash returns ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget.Hash, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget) GetHash() string {
return v.Hash
}
// ModVersionsWithDependenciesResponse is returned by ModVersionsWithDependencies on success.
type ModVersionsWithDependenciesResponse struct {
Mod ModVersionsWithDependenciesMod `json:"mod"`
}
// GetMod returns ModVersionsWithDependenciesResponse.Mod, and is useful for accessing the field via an interface.
func (v *ModVersionsWithDependenciesResponse) GetMod() ModVersionsWithDependenciesMod { return v.Mod }
// ModsModsGetMods includes the requested fields of the GraphQL type GetMods. // ModsModsGetMods includes the requested fields of the GraphQL type GetMods.
type ModsModsGetMods struct { type ModsModsGetMods struct {
Count int `json:"count"` Count int `json:"count"`
@ -545,6 +675,115 @@ const (
OrderDesc Order = "desc" OrderDesc Order = "desc"
) )
// SMLVersionsResponse is returned by SMLVersions on success.
type SMLVersionsResponse struct {
SmlVersions SMLVersionsSmlVersionsGetSMLVersions `json:"smlVersions"`
}
// GetSmlVersions returns SMLVersionsResponse.SmlVersions, and is useful for accessing the field via an interface.
func (v *SMLVersionsResponse) GetSmlVersions() SMLVersionsSmlVersionsGetSMLVersions {
return v.SmlVersions
}
// SMLVersionsSmlVersionsGetSMLVersions includes the requested fields of the GraphQL type GetSMLVersions.
type SMLVersionsSmlVersionsGetSMLVersions struct {
Count int `json:"count"`
Sml_versions []SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion `json:"sml_versions"`
}
// GetCount returns SMLVersionsSmlVersionsGetSMLVersions.Count, and is useful for accessing the field via an interface.
func (v *SMLVersionsSmlVersionsGetSMLVersions) GetCount() int { return v.Count }
// GetSml_versions returns SMLVersionsSmlVersionsGetSMLVersions.Sml_versions, and is useful for accessing the field via an interface.
func (v *SMLVersionsSmlVersionsGetSMLVersions) GetSml_versions() []SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion {
return v.Sml_versions
}
// SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion includes the requested fields of the GraphQL type SMLVersion.
type SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion struct {
Id string `json:"id"`
Version string `json:"version"`
Satisfactory_version int `json:"satisfactory_version"`
Targets []SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVersionTarget `json:"targets"`
}
// GetId returns SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion.Id, and is useful for accessing the field via an interface.
func (v *SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion) GetId() string { return v.Id }
// GetVersion returns SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion.Version, and is useful for accessing the field via an interface.
func (v *SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion) GetVersion() string {
return v.Version
}
// GetSatisfactory_version returns SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion.Satisfactory_version, and is useful for accessing the field via an interface.
func (v *SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion) GetSatisfactory_version() int {
return v.Satisfactory_version
}
// GetTargets returns SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion.Targets, and is useful for accessing the field via an interface.
func (v *SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion) GetTargets() []SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVersionTarget {
return v.Targets
}
// SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVersionTarget includes the requested fields of the GraphQL type SMLVersionTarget.
type SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVersionTarget struct {
TargetName TargetName `json:"targetName"`
Link string `json:"link"`
}
// GetTargetName returns SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVersionTarget.TargetName, and is useful for accessing the field via an interface.
func (v *SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVersionTarget) GetTargetName() TargetName {
return v.TargetName
}
// GetLink returns SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVersionTarget.Link, and is useful for accessing the field via an interface.
func (v *SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersionTargetsSMLVersionTarget) GetLink() string {
return v.Link
}
type TargetName string
const (
TargetNameWindows TargetName = "Windows"
TargetNameWindowsserver TargetName = "WindowsServer"
TargetNameLinuxserver TargetName = "LinuxServer"
)
type VersionFields string
const (
VersionFieldsCreatedAt VersionFields = "created_at"
VersionFieldsUpdatedAt VersionFields = "updated_at"
VersionFieldsDownloads VersionFields = "downloads"
)
type VersionFilter struct {
Limit int `json:"limit,omitempty"`
Offset int `json:"offset,omitempty"`
Order_by VersionFields `json:"order_by,omitempty"`
Order Order `json:"order,omitempty"`
Search string `json:"search,omitempty"`
Ids []string `json:"ids,omitempty"`
}
// GetLimit returns VersionFilter.Limit, and is useful for accessing the field via an interface.
func (v *VersionFilter) GetLimit() int { return v.Limit }
// GetOffset returns VersionFilter.Offset, and is useful for accessing the field via an interface.
func (v *VersionFilter) GetOffset() int { return v.Offset }
// GetOrder_by returns VersionFilter.Order_by, and is useful for accessing the field via an interface.
func (v *VersionFilter) GetOrder_by() VersionFields { return v.Order_by }
// GetOrder returns VersionFilter.Order, and is useful for accessing the field via an interface.
func (v *VersionFilter) GetOrder() Order { return v.Order }
// GetSearch returns VersionFilter.Search, and is useful for accessing the field via an interface.
func (v *VersionFilter) GetSearch() string { return v.Search }
// GetIds returns VersionFilter.Ids, and is useful for accessing the field via an interface.
func (v *VersionFilter) GetIds() []string { return v.Ids }
// VersionMod includes the requested fields of the GraphQL type Mod. // VersionMod includes the requested fields of the GraphQL type Mod.
type VersionMod struct { type VersionMod struct {
Id string `json:"id"` Id string `json:"id"`
@ -645,6 +884,26 @@ type __GetModNameInput struct {
// GetModId returns __GetModNameInput.ModId, and is useful for accessing the field via an interface. // GetModId returns __GetModNameInput.ModId, and is useful for accessing the field via an interface.
func (v *__GetModNameInput) GetModId() string { return v.ModId } func (v *__GetModNameInput) GetModId() string { return v.ModId }
// __ModVersionsInput is used internally by genqlient
type __ModVersionsInput struct {
ModId string `json:"modId,omitempty"`
Filter VersionFilter `json:"filter,omitempty"`
}
// GetModId returns __ModVersionsInput.ModId, and is useful for accessing the field via an interface.
func (v *__ModVersionsInput) GetModId() string { return v.ModId }
// GetFilter returns __ModVersionsInput.Filter, and is useful for accessing the field via an interface.
func (v *__ModVersionsInput) GetFilter() VersionFilter { return v.Filter }
// __ModVersionsWithDependenciesInput is used internally by genqlient
type __ModVersionsWithDependenciesInput struct {
ModId string `json:"modId,omitempty"`
}
// GetModId returns __ModVersionsWithDependenciesInput.ModId, and is useful for accessing the field via an interface.
func (v *__ModVersionsWithDependenciesInput) GetModId() string { return v.ModId }
// __ModsInput is used internally by genqlient // __ModsInput is used internally by genqlient
type __ModsInput struct { type __ModsInput struct {
Filter ModFilter `json:"filter,omitempty"` Filter ModFilter `json:"filter,omitempty"`
@ -870,6 +1129,98 @@ func GetModName(
return &data, err return &data, err
} }
// The query or mutation executed by ModVersions.
const ModVersions_Operation = `
query ModVersions ($modId: String!, $filter: VersionFilter) {
mod: getModByIdOrReference(modIdOrReference: $modId) {
id
versions(filter: $filter) {
id
version
}
}
}
`
func ModVersions(
ctx context.Context,
client graphql.Client,
modId string,
filter VersionFilter,
) (*ModVersionsResponse, error) {
req := &graphql.Request{
OpName: "ModVersions",
Query: ModVersions_Operation,
Variables: &__ModVersionsInput{
ModId: modId,
Filter: filter,
},
}
var err error
var data ModVersionsResponse
resp := &graphql.Response{Data: &data}
err = client.MakeRequest(
ctx,
req,
resp,
)
return &data, err
}
// The query or mutation executed by ModVersionsWithDependencies.
const ModVersionsWithDependencies_Operation = `
query ModVersionsWithDependencies ($modId: String!) {
mod: getModByIdOrReference(modIdOrReference: $modId) {
id
versions(filter: {limit:100}) {
id
version
link
hash
dependencies {
mod_id
condition
optional
}
targets {
targetName
link
hash
}
}
}
}
`
func ModVersionsWithDependencies(
ctx context.Context,
client graphql.Client,
modId string,
) (*ModVersionsWithDependenciesResponse, error) {
req := &graphql.Request{
OpName: "ModVersionsWithDependencies",
Query: ModVersionsWithDependencies_Operation,
Variables: &__ModVersionsWithDependenciesInput{
ModId: modId,
},
}
var err error
var data ModVersionsWithDependenciesResponse
resp := &graphql.Response{Data: &data}
err = client.MakeRequest(
ctx,
req,
resp,
)
return &data, err
}
// The query or mutation executed by Mods. // The query or mutation executed by Mods.
const Mods_Operation = ` const Mods_Operation = `
query Mods ($filter: ModFilter) { query Mods ($filter: ModFilter) {
@ -916,6 +1267,46 @@ func Mods(
return &data, err return &data, err
} }
// The query or mutation executed by SMLVersions.
const SMLVersions_Operation = `
query SMLVersions {
smlVersions: getSMLVersions(filter: {limit:100}) {
count
sml_versions {
id
version
satisfactory_version
targets {
targetName
link
}
}
}
}
`
func SMLVersions(
ctx context.Context,
client graphql.Client,
) (*SMLVersionsResponse, error) {
req := &graphql.Request{
OpName: "SMLVersions",
Query: SMLVersions_Operation,
}
var err error
var data SMLVersionsResponse
resp := &graphql.Response{Data: &data}
err = client.MakeRequest(
ctx,
req,
resp,
)
return &data, err
}
// The query or mutation executed by Version. // The query or mutation executed by Version.
const Version_Operation = ` const Version_Operation = `
query Version ($modId: String!, $version: String!) { query Version ($modId: String!, $version: String!) {

View file

@ -9,10 +9,8 @@ type AllVersionsResponse struct {
type ModVersion struct { type ModVersion struct {
ID string `json:"id"` ID string `json:"id"`
Version string `json:"version"` Version string `json:"version"`
GameVersion string `json:"game_version"`
Dependencies []Dependency `json:"dependencies"` Dependencies []Dependency `json:"dependencies"`
Targets []Target `json:"targets"` Targets []Target `json:"targets"`
RequiredOnRemote bool `json:"required_on_remote"`
} }
type Dependency struct { type Dependency struct {
@ -24,7 +22,6 @@ type Dependency struct {
type Target struct { type Target struct {
VersionID string `json:"version_id"` VersionID string `json:"version_id"`
TargetName string `json:"target_name"` TargetName string `json:"target_name"`
Link string `json:"link"`
Hash string `json:"hash"` Hash string `json:"hash"`
Size int64 `json:"size"` Size int64 `json:"size"`
} }

View file

@ -18,396 +18,18 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_10": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_3": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_4": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_5": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_6": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_7": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_8": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_9": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"mfgames-project-setup": {
"inputs": {
"nixago": "nixago",
"nixago-exts": "nixago-exts_3",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1723051996,
"narHash": "sha256-pcWXdsdAbD0U9b93V/I+eJREbgZ5TWYkFc26gmSz46E=",
"ref": "refs/heads/main",
"rev": "f378dedc71e3b2b82f95041069bec56fce9e9524",
"revCount": 29,
"type": "git",
"url": "https://src.mfgames.com/nixos-contrib/mfgames-project-setup-flake.git"
},
"original": {
"type": "git",
"url": "https://src.mfgames.com/nixos-contrib/mfgames-project-setup-flake.git"
}
},
"nixago": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixago-exts": "nixago-exts",
"nixpkgs": [
"mfgames-project-setup",
"nixpkgs"
]
},
"locked": {
"lastModified": 1687381756,
"narHash": "sha256-IUMIlYfrvj7Yli4H2vvyig8HEPpfCeMaE6+kBGPzFyk=",
"owner": "jmgilman",
"repo": "nixago",
"rev": "dacceb10cace103b3e66552ec9719fa0d33c0dc9",
"type": "github"
},
"original": {
"owner": "jmgilman",
"repo": "nixago",
"type": "github"
}
},
"nixago-exts": {
"inputs": {
"flake-utils": "flake-utils_3",
"nixago": "nixago_2",
"nixpkgs": [
"mfgames-project-setup",
"nixago",
"nixpkgs"
]
},
"locked": {
"lastModified": 1676070308,
"narHash": "sha256-QaJ65oc2l8iwQIGWUJ0EKjCeSuuCM/LqR8RauxZUUkc=",
"owner": "nix-community",
"repo": "nixago-extensions",
"rev": "e5380cb0456f4ea3c86cf94e3039eb856bf07d0b",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixago-extensions",
"type": "github"
}
},
"nixago-exts_2": {
"inputs": {
"flake-utils": "flake-utils_5",
"nixago": "nixago_3",
"nixpkgs": [
"mfgames-project-setup",
"nixago",
"nixago-exts",
"nixago",
"nixpkgs"
]
},
"locked": {
"lastModified": 1655508669,
"narHash": "sha256-BDDdo5dZQMmwNH/GNacy33nPBnCpSIydWFPZs0kkj/g=",
"owner": "nix-community",
"repo": "nixago-extensions",
"rev": "3022a932ce109258482ecc6568c163e8d0b426aa",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixago-extensions",
"type": "github"
}
},
"nixago-exts_3": {
"inputs": {
"flake-utils": "flake-utils_7",
"nixago": "nixago_4",
"nixpkgs": [
"mfgames-project-setup",
"nixpkgs"
]
},
"locked": {
"lastModified": 1676070308,
"narHash": "sha256-QaJ65oc2l8iwQIGWUJ0EKjCeSuuCM/LqR8RauxZUUkc=",
"owner": "nix-community",
"repo": "nixago-extensions",
"rev": "e5380cb0456f4ea3c86cf94e3039eb856bf07d0b",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixago-extensions",
"type": "github"
}
},
"nixago-exts_4": {
"inputs": {
"flake-utils": "flake-utils_9",
"nixago": "nixago_5",
"nixpkgs": [
"mfgames-project-setup",
"nixago-exts",
"nixago",
"nixpkgs"
]
},
"locked": {
"lastModified": 1655508669,
"narHash": "sha256-BDDdo5dZQMmwNH/GNacy33nPBnCpSIydWFPZs0kkj/g=",
"owner": "nix-community",
"repo": "nixago-extensions",
"rev": "3022a932ce109258482ecc6568c163e8d0b426aa",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixago-extensions",
"type": "github"
}
},
"nixago_2": {
"inputs": {
"flake-utils": "flake-utils_4",
"nixago-exts": "nixago-exts_2",
"nixpkgs": [
"mfgames-project-setup",
"nixago",
"nixago-exts",
"nixpkgs"
]
},
"locked": {
"lastModified": 1676070010,
"narHash": "sha256-iYzJIWptE1EUD8VINAg66AAMUajizg8JUYN3oBmb8no=",
"owner": "nix-community",
"repo": "nixago",
"rev": "d480ba6c0c16e2c5c0bd2122852d6a0c9ad1ed0e",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "rename-config-data",
"repo": "nixago",
"type": "github"
}
},
"nixago_3": {
"inputs": {
"flake-utils": "flake-utils_6",
"nixpkgs": [
"mfgames-project-setup",
"nixago",
"nixago-exts",
"nixago",
"nixago-exts",
"nixpkgs"
]
},
"locked": {
"lastModified": 1655405483,
"narHash": "sha256-Crd49aZWNrpczlRTOwWGfwBMsTUoG9vlHDKQC7cx264=",
"owner": "nix-community",
"repo": "nixago",
"rev": "e6a9566c18063db5b120e69e048d3627414e327d",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixago",
"type": "github"
}
},
"nixago_4": {
"inputs": {
"flake-utils": "flake-utils_8",
"nixago-exts": "nixago-exts_4",
"nixpkgs": [
"mfgames-project-setup",
"nixago-exts",
"nixpkgs"
]
},
"locked": {
"lastModified": 1676070010,
"narHash": "sha256-iYzJIWptE1EUD8VINAg66AAMUajizg8JUYN3oBmb8no=",
"owner": "nix-community",
"repo": "nixago",
"rev": "d480ba6c0c16e2c5c0bd2122852d6a0c9ad1ed0e",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "rename-config-data",
"repo": "nixago",
"type": "github"
}
},
"nixago_5": {
"inputs": {
"flake-utils": "flake-utils_10",
"nixpkgs": [
"mfgames-project-setup",
"nixago-exts",
"nixago",
"nixago-exts",
"nixpkgs"
]
},
"locked": {
"lastModified": 1655405483,
"narHash": "sha256-Crd49aZWNrpczlRTOwWGfwBMsTUoG9vlHDKQC7cx264=",
"owner": "nix-community",
"repo": "nixago",
"rev": "e6a9566c18063db5b120e69e048d3627414e327d",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixago",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1706098335, "lastModified": 1700014976,
"narHash": "sha256-r3dWjT8P9/Ah5m5ul4WqIWD8muj5F+/gbCdjiNVBKmU=", "narHash": "sha256-dSGpS2YeJrXW5aH9y7Abd235gGufY3RuZFth6vuyVtU=",
"rev": "a77ab169a83a4175169d78684ddd2e54486ac651", "owner": "NixOS",
"revCount": 554858, "repo": "nixpkgs",
"type": "tarball", "rev": "592047fc9e4f7b74a4dc85d1b9f5243dfe4899e3",
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2311.554858%2Brev-a77ab169a83a4175169d78684ddd2e54486ac651/018d46f0-798f-71dc-a8c5-4689c46f7d12/source.tar.gz" "type": "github"
}, },
"original": { "original": {
"type": "tarball", "id": "nixpkgs",
"url": "https://flakehub.com/f/NixOS/nixpkgs/%2A.tar.gz" "type": "indirect"
} }
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
@ -425,25 +47,10 @@
"type": "indirect" "type": "indirect"
} }
}, },
"nixpkgs_2": {
"locked": {
"lastModified": 1700014976,
"narHash": "sha256-dSGpS2YeJrXW5aH9y7Abd235gGufY3RuZFth6vuyVtU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "592047fc9e4f7b74a4dc85d1b9f5243dfe4899e3",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": { "root": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"mfgames-project-setup": "mfgames-project-setup", "nixpkgs": "nixpkgs",
"nixpkgs": "nixpkgs_2",
"nixpkgs-unstable": "nixpkgs-unstable" "nixpkgs-unstable": "nixpkgs-unstable"
} }
}, },

View file

@ -1,35 +1,19 @@
{ {
description = "ficsit-cli"; description = "smr-cli";
inputs = { inputs = {
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
nixpkgs-unstable.url = "flake:nixpkgs/nixpkgs-unstable"; nixpkgs-unstable.url = "flake:nixpkgs/nixpkgs-unstable";
mfgames-project-setup.url = "git+https://src.mfgames.com/nixos-contrib/mfgames-project-setup-flake.git";
}; };
outputs = { self, nixpkgs, flake-utils, nixpkgs-unstable, mfgames-project-setup }: outputs = { self, nixpkgs, flake-utils, nixpkgs-unstable }:
flake-utils.lib.eachDefaultSystem flake-utils.lib.eachDefaultSystem
(system: (system:
let let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
unstable = nixpkgs-unstable.legacyPackages.${system}; unstable = nixpkgs-unstable.legacyPackages.${system}; in
in
{ {
devShells.default = import ./shell.nix { inherit system pkgs unstable mfgames-project-setup; }; devShells.default = import ./shell.nix { inherit pkgs unstable; };
defaultPackage = pkgs.buildGoModule {
pname = "ficsit-cli";
version = "0.6.0";
doCheck = false; # Tests are failing in this flake.
vendorHash = "sha256-vmA3jvxOLRYj5BmvWMhSEnCTEoe8BLm8lpm2kruIEv4="; #pkgs.lib.fakeHash;
src = pkgs.fetchFromGitHub {
owner = "satisfactorymodding";
repo = "ficsit-cli";
rev = "v0.6.0";
hash = "sha256-Zwidx0war3hos9NEmk9dEzPBgDGdUtWvZb7FIF5OZMA="; #pkgs.lib.fakeHash;
};
};
} }
); );
} }

View file

@ -1,7 +1,7 @@
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml # https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
schema: schema.graphql schema: schema.graphql
operations: operations:
- ficsit/queries/*.graphql - ficsit/queries/*.graphql
generated: ficsit/types.go generated: ficsit/types.go
package: ficsit package: ficsit
bindings: bindings:
@ -17,6 +17,8 @@ bindings:
type: string type: string
GuideID: GuideID:
type: string type: string
SMLVersionID:
type: string
Date: Date:
type: time.Time type: time.Time
unmarshaler: github.com/satisfactorymodding/ficsit-cli/ficsit/utils.UnmarshalDateTime unmarshaler: github.com/satisfactorymodding/ficsit-cli/ficsit/utils.UnmarshalDateTime

29
go.mod
View file

@ -19,20 +19,18 @@ require (
github.com/jackc/puddle/v2 v2.2.1 github.com/jackc/puddle/v2 v2.2.1
github.com/jlaffaye/ftp v0.2.0 github.com/jlaffaye/ftp v0.2.0
github.com/lmittmann/tint v1.0.3 github.com/lmittmann/tint v1.0.3
github.com/mircearoata/pubgrub-go v0.3.3
github.com/muesli/reflow v0.3.0 github.com/muesli/reflow v0.3.0
github.com/pkg/sftp v1.13.6 github.com/pkg/sftp v1.13.6
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/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f
github.com/samber/slog-multi v1.0.2 github.com/samber/slog-multi v1.0.2
github.com/satisfactorymodding/ficsit-resolver v0.0.6 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.1 github.com/spf13/viper v1.18.1
goftp.io/server/v2 v2.0.1 goftp.io/server/v2 v2.0.1
golang.org/x/crypto v0.21.0 golang.org/x/crypto v0.16.0
golang.org/x/sync v0.6.0 golang.org/x/sync v0.5.0
modernc.org/sqlite v1.32.0
) )
require ( require (
@ -56,12 +54,10 @@ require (
github.com/dlclark/regexp2 v1.10.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/css v1.0.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect
@ -73,15 +69,14 @@ require (
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
github.com/microcosm-cc/bluemonday v1.0.26 // indirect github.com/microcosm-cc/bluemonday v1.0.26 // indirect
github.com/mircearoata/pubgrub-go v0.3.3 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.15.2 // indirect github.com/muesli/termenv v0.15.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // 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/pkg/errors v0.9.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 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
@ -99,19 +94,13 @@ require (
github.com/yuin/goldmark-emoji v1.0.2 // indirect github.com/yuin/goldmark-emoji v1.0.2 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect
golang.org/x/mod v0.16.0 // indirect golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.22.0 // indirect golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.18.0 // indirect golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.19.0 // indirect golang.org/x/tools v0.16.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
) )

68
go.sum
View file

@ -86,10 +86,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
@ -103,8 +99,6 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@ -170,8 +164,6 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
@ -195,8 +187,6 @@ github.com/pterm/pterm v0.12.71 h1:KcEJ98EiVCbzDkFbktJ2gMlr4pn8IzyGb9bwK6ffkuA=
github.com/pterm/pterm v0.12.71/go.mod h1:SUAcoZjRt+yjPWlWba+/Fd8zJJ2lSXBQWf0Z0HbFiIQ= github.com/pterm/pterm v0.12.71/go.mod h1:SUAcoZjRt+yjPWlWba+/Fd8zJJ2lSXBQWf0Z0HbFiIQ=
github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew= github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew=
github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
@ -215,8 +205,8 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= 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 h1:6BVH9uHGAsiGkbbtQgAOQJMpKgV8unMrHhhJaw+X1EQ=
github.com/samber/slog-multi v1.0.2/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo= github.com/samber/slog-multi v1.0.2/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo=
github.com/satisfactorymodding/ficsit-resolver v0.0.6 h1:4iCIHOg3z+AvwSVeWtu+k9aysLOL9+FIszCbiKOG2oo= github.com/satisfactorymodding/ficsit-resolver v0.0.2 h1:dj/OsDLpaMUqCHpfBVHvDMUv2nf5gT4HS2ydBMkmtcQ=
github.com/satisfactorymodding/ficsit-resolver v0.0.6/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=
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@ -277,14 +267,14 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
@ -300,13 +290,13 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -328,8 +318,8 @@ 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.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.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -339,8 +329,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -356,8 +346,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
@ -373,29 +363,3 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s=
modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

View file

@ -1,13 +1,6 @@
{ system, pkgs, unstable, mfgames-project-setup }: { pkgs, unstable }:
let
project-config = mfgames-project-setup.lib.mkConfig {
inherit system pkgs;
};
in
pkgs.mkShell {
buildInputs = project-config.packages;
shellHook = project-config.shellHook;
pkgs.mkShell {
nativeBuildInputs = with pkgs.buildPackages; [ nativeBuildInputs = with pkgs.buildPackages; [
unstable.go_1_21 unstable.go_1_21
unstable.golangci-lint unstable.golangci-lint

View file

@ -3,7 +3,6 @@ package components
import ( import (
"github.com/Khan/genqlient/graphql" "github.com/Khan/genqlient/graphql"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/satisfactorymodding/ficsit-cli/cli" "github.com/satisfactorymodding/ficsit-cli/cli"
"github.com/satisfactorymodding/ficsit-cli/cli/provider" "github.com/satisfactorymodding/ficsit-cli/cli/provider"
@ -20,7 +19,6 @@ type RootModel interface {
GetAPIClient() graphql.Client GetAPIClient() graphql.Client
GetProvider() provider.Provider GetProvider() provider.Provider
GetResolver() resolver.DependencyResolver
Size() tea.WindowSizeMsg Size() tea.WindowSizeMsg
SetSize(size tea.WindowSizeMsg) SetSize(size tea.WindowSizeMsg)

View file

@ -7,6 +7,7 @@ import (
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
resolver "github.com/satisfactorymodding/ficsit-resolver" resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/spf13/viper"
"github.com/satisfactorymodding/ficsit-cli/cli" "github.com/satisfactorymodding/ficsit-cli/cli"
"github.com/satisfactorymodding/ficsit-cli/cli/provider" "github.com/satisfactorymodding/ficsit-cli/cli/provider"
@ -28,7 +29,7 @@ func newModel(global *cli.GlobalContext) *rootModel {
Width: 20, Width: 20,
Height: 14, Height: 14,
}, },
dependencyResolver: resolver.NewDependencyResolver(global.Provider), dependencyResolver: resolver.NewDependencyResolver(global.Provider, viper.GetString("api-base")),
} }
m.headerComponent = components.NewHeaderComponent(m) m.headerComponent = components.NewHeaderComponent(m)
@ -70,10 +71,6 @@ func (m *rootModel) GetProvider() provider.Provider {
return m.global.Provider return m.global.Provider
} }
func (m *rootModel) GetResolver() resolver.DependencyResolver {
return m.dependencyResolver
}
func (m *rootModel) Size() tea.WindowSizeMsg { func (m *rootModel) Size() tea.WindowSizeMsg {
return m.currentSize return m.currentSize
} }

View file

@ -13,6 +13,8 @@ import (
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/muesli/reflow/truncate" "github.com/muesli/reflow/truncate"
resolver "github.com/satisfactorymodding/ficsit-resolver"
"github.com/spf13/viper"
"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"
@ -110,7 +112,9 @@ func (m updateModsList) LoadModData() {
return return
} }
updatedLockfile, err := currentProfile.Resolve(m.root.GetResolver(), nil, gameVersion) resolver := resolver.NewDependencyResolver(m.root.GetProvider(), viper.GetString("api-base"))
updatedLockfile, err := currentProfile.Resolve(resolver, nil, gameVersion)
if err != nil { if err != nil {
return return
} }

View file

@ -46,8 +46,6 @@ func main() {
baseLocalDir = os.Getenv("APPDATA") baseLocalDir = os.Getenv("APPDATA")
case "linux": case "linux":
baseLocalDir = filepath.Join(os.Getenv("HOME"), ".local", "share") baseLocalDir = filepath.Join(os.Getenv("HOME"), ".local", "share")
case "darwin":
baseLocalDir = filepath.Join(os.Getenv("HOME"), "Library", "Application Support")
default: default:
panic("unsupported platform: " + runtime.GOOS) panic("unsupported platform: " + runtime.GOOS)
} }