mirror of
https://github.com/nestriness/cli.git
synced 2025-12-11 11:55:35 +02:00
Compare commits
6 Commits
dadff39368
...
feat/run
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed3637a289 | ||
|
|
8bae61077d | ||
|
|
08597cef4b | ||
|
|
a0303bb45f | ||
|
|
5f11ba105b | ||
|
|
e50cf8fc13 |
11
.nestri.yml
Normal file
11
.nestri.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: "0.1"
|
||||||
|
|
||||||
|
games:
|
||||||
|
CyberPunk2047:
|
||||||
|
directory: /path/to/game
|
||||||
|
executable: CyberPunk2047.exe
|
||||||
|
gpu: 1
|
||||||
|
vendor: GPUidhere #(ex: vendor:N) e.g. nvidia:0 or amd:1
|
||||||
|
resolution:
|
||||||
|
height: 1080
|
||||||
|
width: 1920
|
||||||
95
cmd/neofetch.go
Normal file
95
cmd/neofetch.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/charmbracelet/lipgloss/table"
|
||||||
|
"github.com/muesli/termenv"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var neoFetchCmd = &cobra.Command{
|
||||||
|
Use: "neofetch",
|
||||||
|
Short: "Show important system information",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
lipgloss.SetColorProfile(termenv.TrueColor)
|
||||||
|
|
||||||
|
// baseStyle := lipgloss.NewStyle().
|
||||||
|
// MarginTop(1).
|
||||||
|
// MarginRight(4).
|
||||||
|
// MarginBottom(1).
|
||||||
|
// MarginLeft(4)
|
||||||
|
|
||||||
|
var (
|
||||||
|
b strings.Builder
|
||||||
|
lines = strings.Split(art, "\n")
|
||||||
|
colors = []string{"#CC3D00", "#CC3D00"}
|
||||||
|
step = len(lines) / len(colors)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, l := range lines {
|
||||||
|
n := clamp(0, len(colors)-1, i/step)
|
||||||
|
b.WriteString(colorize(colors[n], l))
|
||||||
|
b.WriteRune('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
t := table.New().
|
||||||
|
Border(lipgloss.HiddenBorder()).BorderStyle(lipgloss.NewStyle().Width(3))
|
||||||
|
//TODO: show this specs
|
||||||
|
// info := &specs.Specs{}
|
||||||
|
// infoChan := make(chan specs.Specs, 1)
|
||||||
|
// var wg sync.WaitGroup
|
||||||
|
// wg.Add(1)
|
||||||
|
// go getSpecs(info, infoChan, &wg)
|
||||||
|
// wg.Wait()
|
||||||
|
// newInfo := <-infoChan
|
||||||
|
|
||||||
|
t.Row(b.String())
|
||||||
|
|
||||||
|
fmt.Print(t)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(neoFetchCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// runCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// runCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorize(c, s string) string {
|
||||||
|
return lipgloss.NewStyle().Foreground(lipgloss.Color(c)).Render(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clamp(v, low, high int) int {
|
||||||
|
if high < low {
|
||||||
|
low, high = high, low
|
||||||
|
}
|
||||||
|
return min(high, max(low, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
344
cmd/root.go
344
cmd/root.go
@@ -4,25 +4,30 @@ Copyright © 2024 Nestri <>
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
|
||||||
"github.com/charmbracelet/lipgloss/table"
|
|
||||||
"github.com/docker/docker/api/types/container"
|
|
||||||
"github.com/docker/docker/client"
|
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
|
||||||
"github.com/muesli/termenv"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed nestri.ascii
|
//go:embed nestri.ascii
|
||||||
var art string
|
var art string
|
||||||
|
|
||||||
|
var cfgFile string
|
||||||
|
|
||||||
|
type GameConfig struct {
|
||||||
|
Directory string
|
||||||
|
Executable string
|
||||||
|
GPU int
|
||||||
|
Vendor string
|
||||||
|
Resolution struct {
|
||||||
|
Height int
|
||||||
|
Width int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// rootCmd represents the base command when called without any subcommands
|
// rootCmd represents the base command when called without any subcommands
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "nestri",
|
Use: "nestri",
|
||||||
@@ -33,260 +38,6 @@ var rootCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var neoFetchCmd = &cobra.Command{
|
|
||||||
Use: "neofetch",
|
|
||||||
Short: "Show important system information",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
lipgloss.SetColorProfile(termenv.TrueColor)
|
|
||||||
|
|
||||||
// baseStyle := lipgloss.NewStyle().
|
|
||||||
// MarginTop(1).
|
|
||||||
// MarginRight(4).
|
|
||||||
// MarginBottom(1).
|
|
||||||
// MarginLeft(4)
|
|
||||||
|
|
||||||
var (
|
|
||||||
b strings.Builder
|
|
||||||
lines = strings.Split(art, "\n")
|
|
||||||
colors = []string{"#CC3D00", "#CC3D00"}
|
|
||||||
step = len(lines) / len(colors)
|
|
||||||
)
|
|
||||||
|
|
||||||
for i, l := range lines {
|
|
||||||
n := clamp(0, len(colors)-1, i/step)
|
|
||||||
b.WriteString(colorize(colors[n], l))
|
|
||||||
b.WriteRune('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
t := table.New().
|
|
||||||
Border(lipgloss.HiddenBorder()).BorderStyle(lipgloss.NewStyle().Width(3))
|
|
||||||
//TODO: show this specs
|
|
||||||
// info := &specs.Specs{}
|
|
||||||
// infoChan := make(chan specs.Specs, 1)
|
|
||||||
// var wg sync.WaitGroup
|
|
||||||
// wg.Add(1)
|
|
||||||
// go getSpecs(info, infoChan, &wg)
|
|
||||||
// wg.Wait()
|
|
||||||
// newInfo := <-infoChan
|
|
||||||
|
|
||||||
t.Row(b.String())
|
|
||||||
|
|
||||||
fmt.Print(t)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is the "nestri run" subcommand, takes no arguments for now
|
|
||||||
var runCmd = &cobra.Command{
|
|
||||||
Use: "run",
|
|
||||||
Short: "Run a game using nestri",
|
|
||||||
Args: cobra.MaximumNArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
//make sure os is linux
|
|
||||||
fmt.Println("This command is only supported on Linux.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// var game string
|
|
||||||
// if len(args) > 0 {
|
|
||||||
// game = args[0]
|
|
||||||
// viper.Set("game", game)
|
|
||||||
// viper.WriteConfig()
|
|
||||||
// } else {
|
|
||||||
// game = viper.GetString("game")
|
|
||||||
// if filepath.Ext(game) != ".exe" {
|
|
||||||
// return fmt.Errorf("Make sure the game is a .exe")
|
|
||||||
// }
|
|
||||||
// if game == "" {
|
|
||||||
// return fmt.Errorf("no game specified and no previous game selected")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fmt.Printf("Running game: %s\n\n", game)
|
|
||||||
|
|
||||||
cli, err := client.NewClientWithOpts()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
resp, err := cli.ContainerCreate(ctx, &container.Config{
|
|
||||||
Image: "hello-world",
|
|
||||||
}, nil, nil, nil, "hello-world")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach to the container to get logs
|
|
||||||
out, err := cli.ContainerLogs(ctx, resp.ID, container.LogsOptions{ShowStdout: true, ShowStderr: true, Follow: true})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error attaching to container logs: %s\n", err)
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
// Copy the logs to stdout and stderr
|
|
||||||
stdcopy.StdCopy(os.Stdout, os.Stderr, out)
|
|
||||||
|
|
||||||
// Wait for the container to finish
|
|
||||||
statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
|
|
||||||
select {
|
|
||||||
case err := <-errCh:
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error waiting for container: %s\n", err)
|
|
||||||
}
|
|
||||||
case <-statusCh:
|
|
||||||
fmt.Println("Container finished")
|
|
||||||
}
|
|
||||||
// Clean up the container
|
|
||||||
if err := cli.ContainerRemove(ctx, resp.ID, container.RemoveOptions{}); err != nil {
|
|
||||||
fmt.Printf("Error removing container: %s\n", err)
|
|
||||||
}
|
|
||||||
// if gpu > 0 {
|
|
||||||
// fmt.Print("Using gpu %s\n", gpu)
|
|
||||||
// }
|
|
||||||
// if hdr {
|
|
||||||
// fmt.Println("Enabling HDR mode")
|
|
||||||
// }
|
|
||||||
|
|
||||||
//get linux version
|
|
||||||
// versionCmd := exec.Command("grep", "VERSION", "/etc/os-release")
|
|
||||||
// versionOutput, err := versionCmd.CombinedOutput()
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("error getting linux version:")
|
|
||||||
// }
|
|
||||||
// fmt.Printf("Linux version:\n%s\n", string(versionOutput))
|
|
||||||
|
|
||||||
// //Step 1: change to games dir
|
|
||||||
// fmt.Println("changing to game dir.") //this is a temp command for debug as well as leads to a hardcoded dir
|
|
||||||
|
|
||||||
// HomeDir, err := os.UserHomeDir()
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("error getting home directory %v\n", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// err = os.Chdir(fmt.Sprintf("%s/game", HomeDir))
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("error changing directory: %v\n", err)
|
|
||||||
// }
|
|
||||||
// //verify we are in game dir
|
|
||||||
// dir, err := os.Getwd()
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("error getting current directory: %v\n", err)
|
|
||||||
// }
|
|
||||||
// fmt.Printf("Current directory: %s\n\n", dir)
|
|
||||||
|
|
||||||
// //list games dir
|
|
||||||
// listDir := exec.Command("ls", "-la", ".")
|
|
||||||
// listDirOutput, err := listDir.CombinedOutput()
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Errorf("error listing games: %v\n")
|
|
||||||
// }
|
|
||||||
// fmt.Printf("List of Games: \n%s\n", listDirOutput)
|
|
||||||
|
|
||||||
// //step 2: Generate a Session ID
|
|
||||||
// //generate id
|
|
||||||
// SID := exec.Command("bash", "-c", "head /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 16")
|
|
||||||
|
|
||||||
// //save output to variable
|
|
||||||
// output, err := SID.Output()
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Errorf("Error generating Session ID: %v\n", err)
|
|
||||||
// }
|
|
||||||
// sessionID := strings.TrimSpace(string(output))
|
|
||||||
// fmt.Printf("Your Session ID is: %s\n\n", sessionID)
|
|
||||||
|
|
||||||
// //step 3: Launch netris server
|
|
||||||
// fmt.Println("Installing Netris/Launching Netris Server")
|
|
||||||
// checkRunning := exec.Command("sudo", "docker", "ps", "-q", "-f", "name=netris")
|
|
||||||
// containerId, err := checkRunning.Output()
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("error checking running Docker container: %v", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if len(containerId) == 0 {
|
|
||||||
// checkExisting := exec.Command("sudo", "docker", "ps", "-aq", "-f", "name=netris")
|
|
||||||
// containerId, err = checkExisting.Output()
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("error checking for existing docker container: %v", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if len(containerId) == 0 {
|
|
||||||
// installCmd := exec.Command(
|
|
||||||
// "sudo", "docker", "run", "-d", "--gpus", "all", "--device=/dev/dri",
|
|
||||||
// "--name", "netris", "-it", "--entrypoint", "/bin/bash",
|
|
||||||
// "-e", fmt.Sprintf("SESSION_ID=%s", sessionID),
|
|
||||||
// "-v", fmt.Sprintf("%s:/game", dir), "-p", "8080:8080/udp",
|
|
||||||
// "--cap-add=SYS_NICE", "--cap-add=SYS_ADMIN", "ghcr.io/netrisdotme/netris/server:nightly",
|
|
||||||
// )
|
|
||||||
// installCmd.Stdout = os.Stdout
|
|
||||||
// installCmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
// if err := installCmd.Run(); err != nil {
|
|
||||||
// return fmt.Errorf("error running docker command: %v", err)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// startContainer := exec.Command("sudo", "docker", "start", "netris")
|
|
||||||
// startContainer.Stdout = os.Stdout
|
|
||||||
// startContainer.Stderr = os.Stderr
|
|
||||||
|
|
||||||
// if err := startContainer.Run(); err != nil {
|
|
||||||
// return fmt.Errorf("error starting existing Docker container: %v", err)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //main part of step 4:
|
|
||||||
// //start netris server
|
|
||||||
|
|
||||||
// fmt.Println("starting netris server\n\n")
|
|
||||||
// checkFileCmd := exec.Command("sudo", "docker", "exec", "netris", "ls", "-la", "/tmp")
|
|
||||||
// output, err = checkFileCmd.Output()
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("error checking /tmp dir in docker container: %v\n", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if !strings.Contains(string(output), ".X11-unix") {
|
|
||||||
// startupCmd := exec.Command("sudo", "docker", "exec", "netris", "/etc/startup.sh", ">", "/dev/null", "&")
|
|
||||||
// startupCmd.Stdout = os.Stdout
|
|
||||||
// startupCmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
// if err := startupCmd.Run(); err != nil {
|
|
||||||
// return fmt.Errorf("error running startup command: %v\n", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for {
|
|
||||||
// time.Sleep(7 * time.Minute)
|
|
||||||
// output, err := checkFileCmd.Output()
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("error checking /tmp directory in container: %v\n", err)
|
|
||||||
// }
|
|
||||||
// if strings.Contains(string(output), ".X11-unix") {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// gameCmd := fmt.Sprintf("netris-proton -pr %s", game)
|
|
||||||
// execCmd := exec.Command("sudo", "docker", "exec", "netris", gameCmd)
|
|
||||||
// execCmd.Stdout = os.Stdout
|
|
||||||
// execCmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
// if err := execCmd.Run(); err != nil {
|
|
||||||
// return fmt.Errorf("error executing game command in docker container: %v\n", err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
func Execute() {
|
func Execute() {
|
||||||
@@ -297,56 +48,49 @@ func Execute() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
cobra.OnInitialize(initConfig)
|
||||||
// Here you will define your flags and configuration settings.
|
// Here you will define your flags and configuration settings.
|
||||||
// Cobra supports persistent flags, which, if defined here,
|
// Cobra supports persistent flags, which, if defined here,
|
||||||
// will be global for your application.
|
// will be global for your application.
|
||||||
rootCmd.AddCommand(neoFetchCmd)
|
|
||||||
rootCmd.AddCommand(runCmd)
|
|
||||||
|
|
||||||
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cli.yaml)")
|
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cli.yaml)")
|
||||||
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.nestri.yaml)")
|
||||||
|
|
||||||
// Cobra also supports local flags, which will only run
|
// Cobra also supports local flags, which will only run
|
||||||
// when this action is called directly.
|
// when this action is called directly.
|
||||||
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
}
|
}
|
||||||
|
|
||||||
func colorize(c, s string) string {
|
// initConfig reads in config file and ENV variables if set.
|
||||||
return lipgloss.NewStyle().Foreground(lipgloss.Color(c)).Render(s)
|
func initConfig() {
|
||||||
|
if cfgFile != "" {
|
||||||
|
// Use config file from the flag.
|
||||||
|
viper.SetConfigFile(cfgFile)
|
||||||
|
} else {
|
||||||
|
// Search for config in the current directory
|
||||||
|
viper.AddConfigPath(".")
|
||||||
|
viper.SetConfigName(".nestri")
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
|
||||||
|
// If not found in current directory, check in $HOME/.nestri/
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
// Find home directory.
|
||||||
|
home, err := os.UserHomeDir()
|
||||||
|
cobra.CheckErr(err)
|
||||||
|
|
||||||
|
// Search config in home directory with name ".nestri" (without extension).
|
||||||
|
viper.AddConfigPath(home)
|
||||||
|
viper.SetConfigType("yaml")
|
||||||
|
viper.SetConfigName(".nestri")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func clamp(v, low, high int) int {
|
|
||||||
if high < low {
|
|
||||||
low, high = high, low
|
|
||||||
}
|
|
||||||
return min(high, max(low, v))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func min(a, b int) int {
|
viper.AutomaticEnv() // read in environment variables that match
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func max(a, b int) int {
|
// If a config file is found, read it in.
|
||||||
if a > b {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
return a
|
fmt.Fprintln(os.Stderr, "Could not find a config file in local directory or in $HOME directory:")
|
||||||
}
|
}
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// func getSpecs(info *specs.Specs, infoChan chan specs.Specs, wg *sync.WaitGroup) {
|
|
||||||
// defer wg.Done()
|
|
||||||
// sys := specs.New()
|
|
||||||
// // info.Userhost = getUserHostname()
|
|
||||||
// // info.OS = getOSName()
|
|
||||||
// // info.Kernel = getKernelVersion()
|
|
||||||
// // info.Uptime = getUptime()
|
|
||||||
// // info.Shell = getShell()
|
|
||||||
// // info.CPU = getCPUName()
|
|
||||||
// // info.RAM = getMemStats()
|
|
||||||
// info.GPU, _ = sys.GetGPUInfo()
|
|
||||||
// // info.SystemArch, _ = getSystemArch()
|
|
||||||
// // info.DiskUsage, _ = getDiskUsage()
|
|
||||||
// infoChan <- *info
|
|
||||||
// }
|
|
||||||
|
|||||||
90
cmd/run.go
Normal file
90
cmd/run.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// this is the "nestri run" subcommand, takes no arguments for now
|
||||||
|
var runCmd = &cobra.Command{
|
||||||
|
Use: "run",
|
||||||
|
Short: "Run a game using nestri",
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
//make sure os is linux
|
||||||
|
fmt.Println("This command is only supported on Linux.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//The main job here is to:
|
||||||
|
//1. look for an exe in a certain directory,
|
||||||
|
//2. mount the directory inside the container
|
||||||
|
//3. Run the nestri docker container
|
||||||
|
//4. SSH into the container and set up everything
|
||||||
|
//5. Run the game
|
||||||
|
//6. Provide the URL to play or throw an error otherwise.
|
||||||
|
|
||||||
|
// The last argument is the game to run
|
||||||
|
game := args[len(args)-1]
|
||||||
|
|
||||||
|
// Load game configuration
|
||||||
|
var gameConfig GameConfig
|
||||||
|
if err := viper.UnmarshalKey(fmt.Sprintf("games.%s", game), &gameConfig); err != nil {
|
||||||
|
return fmt.Errorf("error parsing game configuration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
|
||||||
|
if flags.Changed("directory") || flags.Changed("d") {
|
||||||
|
gameConfig.Directory, _ = flags.GetString("directory")
|
||||||
|
}
|
||||||
|
if flags.Changed("executable") || flags.Changed("x") {
|
||||||
|
gameConfig.Executable, _ = flags.GetString("executable")
|
||||||
|
}
|
||||||
|
if flags.Changed("gpu") {
|
||||||
|
gameConfig.GPU, _ = flags.GetInt("gpu")
|
||||||
|
}
|
||||||
|
if flags.Changed("vendor") || flags.Changed("v") {
|
||||||
|
gameConfig.Vendor, _ = flags.GetString("vendor")
|
||||||
|
}
|
||||||
|
if flags.Changed("height") || flags.Changed("H") {
|
||||||
|
gameConfig.Resolution.Height, _ = flags.GetInt("height")
|
||||||
|
}
|
||||||
|
if flags.Changed("width") || flags.Changed("W") {
|
||||||
|
gameConfig.Resolution.Width, _ = flags.GetInt("width")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Game config:", gameConfig)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(runCmd)
|
||||||
|
|
||||||
|
runCmd.Flags().StringP("directory", "d", "", "Game directory")
|
||||||
|
runCmd.Flags().StringP("executable", "x", "", "Game executable")
|
||||||
|
runCmd.Flags().Int("gpu", 0, "GPU number")
|
||||||
|
runCmd.Flags().StringP("vendor", "v", "", "GPU vendor")
|
||||||
|
runCmd.Flags().IntP("height", "H", 1080, "Screen height")
|
||||||
|
runCmd.Flags().IntP("width", "W", 1920, "Screen width")
|
||||||
|
|
||||||
|
// viper.BindPFlag("directory", runCmd.Flags().Lookup("directory"))
|
||||||
|
// viper.BindPFlag("executable", runCmd.Flags().Lookup("executable"))
|
||||||
|
// viper.BindPFlag("gpu", runCmd.Flags().Lookup("gpu"))
|
||||||
|
// viper.BindPFlag("vendor", runCmd.Flags().Lookup("vendor"))
|
||||||
|
// viper.BindPFlag("resolution.height", runCmd.Flags().Lookup("height"))
|
||||||
|
// viper.BindPFlag("resolution.width", runCmd.Flags().Lookup("width"))
|
||||||
|
|
||||||
|
viper.BindPFlag("games.*.directory", runCmd.Flags().Lookup("directory"))
|
||||||
|
viper.BindPFlag("games.*.executable", runCmd.Flags().Lookup("executable"))
|
||||||
|
viper.BindPFlag("games.*.gpu", runCmd.Flags().Lookup("gpu"))
|
||||||
|
viper.BindPFlag("games.*.vendor", runCmd.Flags().Lookup("vendor"))
|
||||||
|
viper.BindPFlag("games.*.resolution.height", runCmd.Flags().Lookup("height"))
|
||||||
|
viper.BindPFlag("games.*.resolution.width", runCmd.Flags().Lookup("width"))
|
||||||
|
}
|
||||||
39
go.mod
39
go.mod
@@ -4,26 +4,15 @@ go 1.22.2
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/charmbracelet/lipgloss v0.11.0
|
github.com/charmbracelet/lipgloss v0.11.0
|
||||||
github.com/moby/moby v27.0.2+incompatible
|
|
||||||
github.com/muesli/termenv v0.15.2
|
github.com/muesli/termenv v0.15.2
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.1.1 // indirect
|
github.com/charmbracelet/x/ansi v0.1.2 // indirect
|
||||||
github.com/containerd/log v0.1.0 // indirect
|
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
|
||||||
github.com/docker/docker v27.0.2+incompatible // indirect
|
|
||||||
github.com/docker/go-connections v0.5.0 // indirect
|
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
|
||||||
github.com/gogo/protobuf v1.3.2 // 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/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
@@ -31,33 +20,19 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
|
||||||
github.com/moby/term v0.5.0 // indirect
|
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
|
||||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.27.0 // indirect
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.27.0 // indirect
|
golang.org/x/text v0.16.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.27.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/trace v1.27.0 // indirect
|
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
|
||||||
golang.org/x/text v0.15.0 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gotest.tools/v3 v3.5.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
143
go.sum
143
go.sum
@@ -1,54 +1,24 @@
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
|
||||||
github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
|
github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
|
||||||
github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
|
github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
|
||||||
github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk=
|
github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY=
|
||||||
github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
|
||||||
github.com/docker/docker v27.0.2+incompatible h1:mNhCtgXNV1fIRns102grG7rdzIsGGCq1OlOD0KunZos=
|
|
||||||
github.com/docker/docker v27.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
|
||||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
|
||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
|
||||||
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/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
|
||||||
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=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@@ -63,25 +33,10 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
|
|||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
|
||||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
|
||||||
github.com/moby/moby v27.0.2+incompatible h1:iYtGEjFi9lkX2m/Bop2H/peXzx3VtzmPlE9r0JHyH0s=
|
|
||||||
github.com/moby/moby v27.0.2+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
|
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
|
||||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
|
||||||
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/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
|
||||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
|
||||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@@ -91,32 +46,26 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
|
|||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
@@ -124,75 +73,15 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
|||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
|
|
||||||
go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY=
|
|
||||||
go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
|
|
||||||
go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI=
|
|
||||||
go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A=
|
|
||||||
go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
|
|
||||||
go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
|
|
||||||
go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=
|
|
||||||
go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
|
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
|
||||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
|
||||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
|
||||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
|
||||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
|
||||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
|
||||||
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=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -201,5 +90,3 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
|
||||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
|
||||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
|
||||||
|
|||||||
302
root.go
302
root.go
@@ -1,302 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright © 2024 Nestri <>
|
|
||||||
*/
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
|
||||||
"github.com/charmbracelet/lipgloss/table"
|
|
||||||
"github.com/muesli/termenv"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed nestri.ascii
|
|
||||||
var art string
|
|
||||||
|
|
||||||
var (
|
|
||||||
gpu int
|
|
||||||
hdr bool
|
|
||||||
)
|
|
||||||
|
|
||||||
// rootCmd represents the base command when called without any subcommands
|
|
||||||
// For a good reference point, start here: https://github.com/charmbracelet/taskcli/blob/main/cmds.go
|
|
||||||
var rootCmd = &cobra.Command{
|
|
||||||
Use: "nestri",
|
|
||||||
Short: "A CLI tool to manage your cloud gaming service",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
return cmd.Help()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is for the "nestri neofetch" subcommand, has no arguments
|
|
||||||
var neoFetchCmd = &cobra.Command{
|
|
||||||
Use: "neofetch",
|
|
||||||
Short: "Show important system information",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
lipgloss.SetColorProfile(termenv.TrueColor)
|
|
||||||
|
|
||||||
baseStyle := lipgloss.NewStyle().
|
|
||||||
PaddingTop(1).
|
|
||||||
PaddingRight(4).
|
|
||||||
PaddingBottom(1).
|
|
||||||
PaddingLeft(4)
|
|
||||||
|
|
||||||
var (
|
|
||||||
b strings.Builder
|
|
||||||
lines = strings.Split(art, "\n")
|
|
||||||
colors = []string{"#F8481C", "#F74127", "#F53B30", "#F23538", "#F02E40"}
|
|
||||||
step = len(lines) / len(colors)
|
|
||||||
)
|
|
||||||
|
|
||||||
for i, l := range lines {
|
|
||||||
n := clamp(0, len(colors)-1, i/step)
|
|
||||||
b.WriteString(colorize(colors[n], l))
|
|
||||||
b.WriteRune('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
t := table.New().
|
|
||||||
Border(lipgloss.HiddenBorder())
|
|
||||||
|
|
||||||
t.Row(baseStyle.Render(b.String()), baseStyle.Render("System Info goes here"))
|
|
||||||
|
|
||||||
fmt.Print(t)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is the "nestri run" subcommand, takes no arguments for now
|
|
||||||
var runCmd = &cobra.Command{
|
|
||||||
Use: "run",
|
|
||||||
Short: "Run a game using nestri",
|
|
||||||
Args: cobra.MaximumNArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
//make sure os is linux
|
|
||||||
fmt.Println("This command is only supported on Linux.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var game string
|
|
||||||
if len(args) > 0 {
|
|
||||||
game = args[0]
|
|
||||||
viper.Set("game", game)
|
|
||||||
viper.WriteConfig()
|
|
||||||
} else {
|
|
||||||
game = viper.GetString("game")
|
|
||||||
if filepath.Ext(game) != ".exe" {
|
|
||||||
return fmt.Errorf("Make sure the game is a .exe")
|
|
||||||
}
|
|
||||||
if game == "" {
|
|
||||||
return fmt.Errorf("no game specified and no previous game selected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Running game: %s\n\n", game)
|
|
||||||
if gpu > 0 {
|
|
||||||
fmt.Print("Using gpu %s\n", gpu)
|
|
||||||
}
|
|
||||||
if hdr {
|
|
||||||
fmt.Println("Enabling HDR mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
//get linux version
|
|
||||||
versionCmd := exec.Command("grep", "VERSION", "/etc/os-release")
|
|
||||||
versionOutput, err := versionCmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error getting linux version:")
|
|
||||||
}
|
|
||||||
fmt.Printf("Linux version:\n%s\n", string(versionOutput))
|
|
||||||
|
|
||||||
//Step 1: change to games dir
|
|
||||||
fmt.Println("changing to game dir.") //this is a temp command for debug as well as leads to a hardcoded dir
|
|
||||||
|
|
||||||
HomeDir, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error getting home directory %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.Chdir(fmt.Sprintf("%s/game", HomeDir))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error changing directory: %v\n", err)
|
|
||||||
}
|
|
||||||
//verify we are in game dir
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error getting current directory: %v\n", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("Current directory: %s\n\n", dir)
|
|
||||||
|
|
||||||
//list games dir
|
|
||||||
listDir := exec.Command("ls", "-la", ".")
|
|
||||||
listDirOutput, err := listDir.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Errorf("error listing games: %v\n")
|
|
||||||
}
|
|
||||||
fmt.Printf("List of Games: \n%s\n", listDirOutput)
|
|
||||||
|
|
||||||
//step 2: Generate a Session ID
|
|
||||||
//generate id
|
|
||||||
SID := exec.Command("bash", "-c", "head /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 16")
|
|
||||||
|
|
||||||
//save output to variable
|
|
||||||
output, err := SID.Output()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Errorf("Error generating Session ID: %v\n", err)
|
|
||||||
}
|
|
||||||
sessionID := strings.TrimSpace(string(output))
|
|
||||||
fmt.Printf("Your Session ID is: %s\n\n", sessionID)
|
|
||||||
|
|
||||||
//step 3: Launch netris server
|
|
||||||
fmt.Println("Installing Netris/Launching Netris Server\n")
|
|
||||||
checkRunning := exec.Command("sudo", "docker", "ps", "-q", "-f", "name=netris")
|
|
||||||
containerId, err := checkRunning.Output()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error checking running Docker container: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(containerId) == 0 {
|
|
||||||
checkExisting := exec.Command("sudo", "docker", "ps", "-aq", "-f", "name=netris")
|
|
||||||
containerId, err = checkExisting.Output()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error checking for existing docker container: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(containerId) == 0 {
|
|
||||||
installCmd := exec.Command(
|
|
||||||
"sudo", "docker", "run", "-d", "--gpus", "all", "--device=/dev/dri",
|
|
||||||
"--name", "netris", "-it", "--entrypoint", "/bin/bash",
|
|
||||||
"-e", fmt.Sprintf("SESSION_ID=%s", sessionID),
|
|
||||||
"-v", fmt.Sprintf("%s:/game", dir), "-p", "8080:8080/udp",
|
|
||||||
"--cap-add=SYS_NICE", "--cap-add=SYS_ADMIN", "ghcr.io/netrisdotme/netris/server:nightly",
|
|
||||||
)
|
|
||||||
installCmd.Stdout = os.Stdout
|
|
||||||
installCmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
if err := installCmd.Run(); err != nil {
|
|
||||||
return fmt.Errorf("error running docker command: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
startContainer := exec.Command("sudo", "docker", "start", "netris")
|
|
||||||
startContainer.Stdout = os.Stdout
|
|
||||||
startContainer.Stderr = os.Stderr
|
|
||||||
|
|
||||||
if err := startContainer.Run(); err != nil {
|
|
||||||
return fmt.Errorf("error starting existing Docker container: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//main part of step 4:
|
|
||||||
//start netris server
|
|
||||||
|
|
||||||
fmt.Println("starting netris server\n\n")
|
|
||||||
checkFileCmd := exec.Command("sudo", "docker", "exec", "netris", "ls", "-la", "/tmp")
|
|
||||||
output, err = checkFileCmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error checking /tmp dir in docker container: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(string(output), ".X11-unix") {
|
|
||||||
startupCmd := exec.Command("sudo", "docker", "exec", "netris", "/etc/startup.sh", ">", "/dev/null", "&")
|
|
||||||
startupCmd.Stdout = os.Stdout
|
|
||||||
startupCmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
if err := startupCmd.Run(); err != nil {
|
|
||||||
return fmt.Errorf("error running startup command: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
time.Sleep(7 * time.Minute)
|
|
||||||
output, err := checkFileCmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error checking /tmp directory in container: %v\n", err)
|
|
||||||
}
|
|
||||||
if strings.Contains(string(output), ".X11-unix") {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gameCmd := fmt.Sprintf("netris-proton -pr %s", game)
|
|
||||||
execCmd := exec.Command("sudo", "docker", "exec", "netris", gameCmd)
|
|
||||||
execCmd.Stdout = os.Stdout
|
|
||||||
execCmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
if err := execCmd.Run(); err != nil {
|
|
||||||
return fmt.Errorf("error executing game command in docker container: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
|
||||||
func Execute() {
|
|
||||||
err := rootCmd.Execute()
|
|
||||||
if err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
rootCmd.AddCommand(neoFetchCmd)
|
|
||||||
|
|
||||||
rootCmd.AddCommand(runCmd)
|
|
||||||
|
|
||||||
runCmd.Flags().IntVar(&gpu, "gpu", 0, "Specify GPU index")
|
|
||||||
runCmd.Flags().BoolVar(&hdr, "hdr", false, "Enable HDR mode")
|
|
||||||
|
|
||||||
viper.SetConfigName("config")
|
|
||||||
viper.SetConfigType("yaml")
|
|
||||||
viper.AddConfigPath(".")
|
|
||||||
|
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
|
||||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
fmt.Println("error reading config file: %v(you should be able to ignore this)", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If you want to add subcommands to run for example "netri run -fsr" do it like this
|
|
||||||
// runCmd.Flags().BoolP("fsr", "f", false, "Run the Game with FSR enabled or not")
|
|
||||||
}
|
|
||||||
|
|
||||||
func colorize(c, s string) string {
|
|
||||||
return lipgloss.NewStyle().Foreground(lipgloss.Color(c)).Render(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func clamp(v, low, high int) int {
|
|
||||||
if high < low {
|
|
||||||
low, high = high, low
|
|
||||||
}
|
|
||||||
return min(high, max(low, v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func min(a, b int) int {
|
|
||||||
if a < b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func max(a, b int) int {
|
|
||||||
if a > b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
302
root.go.txt
Normal file
302
root.go.txt
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
// /*
|
||||||
|
// Copyright © 2024 Nestri <>
|
||||||
|
// */
|
||||||
|
// package main
|
||||||
|
|
||||||
|
// import (
|
||||||
|
// _ "embed"
|
||||||
|
// "fmt"
|
||||||
|
// "os"
|
||||||
|
// "os/exec"
|
||||||
|
// "path/filepath"
|
||||||
|
// "runtime"
|
||||||
|
// "strings"
|
||||||
|
// "time"
|
||||||
|
|
||||||
|
// "github.com/charmbracelet/lipgloss"
|
||||||
|
// "github.com/charmbracelet/lipgloss/table"
|
||||||
|
// "github.com/muesli/termenv"
|
||||||
|
// "github.com/spf13/cobra"
|
||||||
|
// "github.com/spf13/viper"
|
||||||
|
// )
|
||||||
|
|
||||||
|
// //go:embed nestri.ascii
|
||||||
|
// var art string
|
||||||
|
|
||||||
|
// var (
|
||||||
|
// gpu int
|
||||||
|
// hdr bool
|
||||||
|
// )
|
||||||
|
|
||||||
|
// // rootCmd represents the base command when called without any subcommands
|
||||||
|
// // For a good reference point, start here: https://github.com/charmbracelet/taskcli/blob/main/cmds.go
|
||||||
|
// var rootCmd = &cobra.Command{
|
||||||
|
// Use: "nestri",
|
||||||
|
// Short: "A CLI tool to manage your cloud gaming service",
|
||||||
|
// Args: cobra.NoArgs,
|
||||||
|
// RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// return cmd.Help()
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // this is for the "nestri neofetch" subcommand, has no arguments
|
||||||
|
// var neoFetchCmd = &cobra.Command{
|
||||||
|
// Use: "neofetch",
|
||||||
|
// Short: "Show important system information",
|
||||||
|
// Args: cobra.NoArgs,
|
||||||
|
// RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// lipgloss.SetColorProfile(termenv.TrueColor)
|
||||||
|
|
||||||
|
// baseStyle := lipgloss.NewStyle().
|
||||||
|
// PaddingTop(1).
|
||||||
|
// PaddingRight(4).
|
||||||
|
// PaddingBottom(1).
|
||||||
|
// PaddingLeft(4)
|
||||||
|
|
||||||
|
// var (
|
||||||
|
// b strings.Builder
|
||||||
|
// lines = strings.Split(art, "\n")
|
||||||
|
// colors = []string{"#F8481C", "#F74127", "#F53B30", "#F23538", "#F02E40"}
|
||||||
|
// step = len(lines) / len(colors)
|
||||||
|
// )
|
||||||
|
|
||||||
|
// for i, l := range lines {
|
||||||
|
// n := clamp(0, len(colors)-1, i/step)
|
||||||
|
// b.WriteString(colorize(colors[n], l))
|
||||||
|
// b.WriteRune('\n')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// t := table.New().
|
||||||
|
// Border(lipgloss.HiddenBorder())
|
||||||
|
|
||||||
|
// t.Row(baseStyle.Render(b.String()), baseStyle.Render("System Info goes here"))
|
||||||
|
|
||||||
|
// fmt.Print(t)
|
||||||
|
|
||||||
|
// return nil
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // this is the "nestri run" subcommand, takes no arguments for now
|
||||||
|
// var runCmd = &cobra.Command{
|
||||||
|
// Use: "run",
|
||||||
|
// Short: "Run a game using nestri",
|
||||||
|
// Args: cobra.MaximumNArgs(1),
|
||||||
|
// RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// if runtime.GOOS != "linux" {
|
||||||
|
// //make sure os is linux
|
||||||
|
// fmt.Println("This command is only supported on Linux.")
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var game string
|
||||||
|
// if len(args) > 0 {
|
||||||
|
// game = args[0]
|
||||||
|
// viper.Set("game", game)
|
||||||
|
// viper.WriteConfig()
|
||||||
|
// } else {
|
||||||
|
// game = viper.GetString("game")
|
||||||
|
// if filepath.Ext(game) != ".exe" {
|
||||||
|
// return fmt.Errorf("Make sure the game is a .exe")
|
||||||
|
// }
|
||||||
|
// if game == "" {
|
||||||
|
// return fmt.Errorf("no game specified and no previous game selected")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fmt.Printf("Running game: %s\n\n", game)
|
||||||
|
// if gpu > 0 {
|
||||||
|
// fmt.Print("Using gpu %s\n", gpu)
|
||||||
|
// }
|
||||||
|
// if hdr {
|
||||||
|
// fmt.Println("Enabling HDR mode")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //get linux version
|
||||||
|
// versionCmd := exec.Command("grep", "VERSION", "/etc/os-release")
|
||||||
|
// versionOutput, err := versionCmd.CombinedOutput()
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error getting linux version:")
|
||||||
|
// }
|
||||||
|
// fmt.Printf("Linux version:\n%s\n", string(versionOutput))
|
||||||
|
|
||||||
|
// //Step 1: change to games dir
|
||||||
|
// fmt.Println("changing to game dir.") //this is a temp command for debug as well as leads to a hardcoded dir
|
||||||
|
|
||||||
|
// HomeDir, err := os.UserHomeDir()
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error getting home directory %v\n", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// err = os.Chdir(fmt.Sprintf("%s/game", HomeDir))
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error changing directory: %v\n", err)
|
||||||
|
// }
|
||||||
|
// //verify we are in game dir
|
||||||
|
// dir, err := os.Getwd()
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error getting current directory: %v\n", err)
|
||||||
|
// }
|
||||||
|
// fmt.Printf("Current directory: %s\n\n", dir)
|
||||||
|
|
||||||
|
// //list games dir
|
||||||
|
// listDir := exec.Command("ls", "-la", ".")
|
||||||
|
// listDirOutput, err := listDir.CombinedOutput()
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Errorf("error listing games: %v\n")
|
||||||
|
// }
|
||||||
|
// fmt.Printf("List of Games: \n%s\n", listDirOutput)
|
||||||
|
|
||||||
|
// //step 2: Generate a Session ID
|
||||||
|
// //generate id
|
||||||
|
// SID := exec.Command("bash", "-c", "head /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 16")
|
||||||
|
|
||||||
|
// //save output to variable
|
||||||
|
// output, err := SID.Output()
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Errorf("Error generating Session ID: %v\n", err)
|
||||||
|
// }
|
||||||
|
// sessionID := strings.TrimSpace(string(output))
|
||||||
|
// fmt.Printf("Your Session ID is: %s\n\n", sessionID)
|
||||||
|
|
||||||
|
// //step 3: Launch netris server
|
||||||
|
// fmt.Println("Installing Netris/Launching Netris Server\n")
|
||||||
|
// checkRunning := exec.Command("sudo", "docker", "ps", "-q", "-f", "name=netris")
|
||||||
|
// containerId, err := checkRunning.Output()
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error checking running Docker container: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if len(containerId) == 0 {
|
||||||
|
// checkExisting := exec.Command("sudo", "docker", "ps", "-aq", "-f", "name=netris")
|
||||||
|
// containerId, err = checkExisting.Output()
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error checking for existing docker container: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if len(containerId) == 0 {
|
||||||
|
// installCmd := exec.Command(
|
||||||
|
// "sudo", "docker", "run", "-d", "--gpus", "all", "--device=/dev/dri",
|
||||||
|
// "--name", "netris", "-it", "--entrypoint", "/bin/bash",
|
||||||
|
// "-e", fmt.Sprintf("SESSION_ID=%s", sessionID),
|
||||||
|
// "-v", fmt.Sprintf("%s:/game", dir), "-p", "8080:8080/udp",
|
||||||
|
// "--cap-add=SYS_NICE", "--cap-add=SYS_ADMIN", "ghcr.io/netrisdotme/netris/server:nightly",
|
||||||
|
// )
|
||||||
|
// installCmd.Stdout = os.Stdout
|
||||||
|
// installCmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// if err := installCmd.Run(); err != nil {
|
||||||
|
// return fmt.Errorf("error running docker command: %v", err)
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// startContainer := exec.Command("sudo", "docker", "start", "netris")
|
||||||
|
// startContainer.Stdout = os.Stdout
|
||||||
|
// startContainer.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// if err := startContainer.Run(); err != nil {
|
||||||
|
// return fmt.Errorf("error starting existing Docker container: %v", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //main part of step 4:
|
||||||
|
// //start netris server
|
||||||
|
|
||||||
|
// fmt.Println("starting netris server\n\n")
|
||||||
|
// checkFileCmd := exec.Command("sudo", "docker", "exec", "netris", "ls", "-la", "/tmp")
|
||||||
|
// output, err = checkFileCmd.Output()
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error checking /tmp dir in docker container: %v\n", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if !strings.Contains(string(output), ".X11-unix") {
|
||||||
|
// startupCmd := exec.Command("sudo", "docker", "exec", "netris", "/etc/startup.sh", ">", "/dev/null", "&")
|
||||||
|
// startupCmd.Stdout = os.Stdout
|
||||||
|
// startupCmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// if err := startupCmd.Run(); err != nil {
|
||||||
|
// return fmt.Errorf("error running startup command: %v\n", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for {
|
||||||
|
// time.Sleep(7 * time.Minute)
|
||||||
|
// output, err := checkFileCmd.Output()
|
||||||
|
// if err != nil {
|
||||||
|
// return fmt.Errorf("error checking /tmp directory in container: %v\n", err)
|
||||||
|
// }
|
||||||
|
// if strings.Contains(string(output), ".X11-unix") {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// gameCmd := fmt.Sprintf("netris-proton -pr %s", game)
|
||||||
|
// execCmd := exec.Command("sudo", "docker", "exec", "netris", gameCmd)
|
||||||
|
// execCmd.Stdout = os.Stdout
|
||||||
|
// execCmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// if err := execCmd.Run(); err != nil {
|
||||||
|
// return fmt.Errorf("error executing game command in docker container: %v\n", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return nil
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
// // This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
|
// func Execute() {
|
||||||
|
// err := rootCmd.Execute()
|
||||||
|
// if err != nil {
|
||||||
|
// os.Exit(1)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func init() {
|
||||||
|
// rootCmd.AddCommand(neoFetchCmd)
|
||||||
|
|
||||||
|
// rootCmd.AddCommand(runCmd)
|
||||||
|
|
||||||
|
// runCmd.Flags().IntVar(&gpu, "gpu", 0, "Specify GPU index")
|
||||||
|
// runCmd.Flags().BoolVar(&hdr, "hdr", false, "Enable HDR mode")
|
||||||
|
|
||||||
|
// viper.SetConfigName("config")
|
||||||
|
// viper.SetConfigType("yaml")
|
||||||
|
// viper.AddConfigPath(".")
|
||||||
|
|
||||||
|
// if err := viper.ReadInConfig(); err != nil {
|
||||||
|
// if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// fmt.Println("error reading config file: %v(you should be able to ignore this)", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //If you want to add subcommands to run for example "netri run -fsr" do it like this
|
||||||
|
// // runCmd.Flags().BoolP("fsr", "f", false, "Run the Game with FSR enabled or not")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func colorize(c, s string) string {
|
||||||
|
// return lipgloss.NewStyle().Foreground(lipgloss.Color(c)).Render(s)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func clamp(v, low, high int) int {
|
||||||
|
// if high < low {
|
||||||
|
// low, high = high, low
|
||||||
|
// }
|
||||||
|
// return min(high, max(low, v))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func min(a, b int) int {
|
||||||
|
// if a < b {
|
||||||
|
// return a
|
||||||
|
// }
|
||||||
|
// return b
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func max(a, b int) int {
|
||||||
|
// if a > b {
|
||||||
|
// return a
|
||||||
|
// }
|
||||||
|
// return b
|
||||||
|
// }
|
||||||
Reference in New Issue
Block a user