interactivity with huh
This commit is contained in:
3
go.mod
3
go.mod
@ -2,6 +2,8 @@ module shrunner
|
|||||||
|
|
||||||
go 1.24.3
|
go 1.24.3
|
||||||
|
|
||||||
|
require github.com/charmbracelet/huh v0.7.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
@ -9,7 +11,6 @@ require (
|
|||||||
github.com/charmbracelet/bubbles v0.21.0 // indirect
|
github.com/charmbracelet/bubbles v0.21.0 // indirect
|
||||||
github.com/charmbracelet/bubbletea v1.3.4 // indirect
|
github.com/charmbracelet/bubbletea v1.3.4 // indirect
|
||||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||||
github.com/charmbracelet/huh v0.7.0 // indirect
|
|
||||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||||
|
18
go.sum
18
go.sum
@ -1,7 +1,11 @@
|
|||||||
|
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||||
|
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||||
|
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
||||||
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
|
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
|
||||||
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
||||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||||
@ -18,10 +22,22 @@ github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2ll
|
|||||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||||
|
github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
|
||||||
|
github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
|
||||||
|
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA=
|
||||||
|
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
|
||||||
|
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||||
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
|
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
|
||||||
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
|
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
|
||||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
||||||
|
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
||||||
|
github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI=
|
||||||
|
github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4=
|
||||||
|
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||||
|
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||||
@ -47,6 +63,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
128
main.go
128
main.go
@ -5,7 +5,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/huh"
|
||||||
)
|
)
|
||||||
|
|
||||||
// maxRedirects defines the maximum number of redirects to follow.
|
// maxRedirects defines the maximum number of redirects to follow.
|
||||||
@ -21,7 +24,6 @@ func isValidURLAndFetchContent(url string, redirectCount int) ([]byte, bool, err
|
|||||||
|
|
||||||
// Check if the URL ends with .sh
|
// Check if the URL ends with .sh
|
||||||
if strings.HasSuffix(strings.ToLower(url), ".sh") {
|
if strings.HasSuffix(strings.ToLower(url), ".sh") {
|
||||||
// If it ends with .sh, try to download it directly
|
|
||||||
fmt.Printf("Attempting to download: %s\n", url)
|
fmt.Printf("Attempting to download: %s\n", url)
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -36,11 +38,9 @@ func isValidURLAndFetchContent(url string, redirectCount int) ([]byte, bool, err
|
|||||||
}
|
}
|
||||||
return content, true, nil
|
return content, true, nil
|
||||||
}
|
}
|
||||||
// If status is not OK, but it ended with .sh, we treat it as a failed download for a .sh file.
|
|
||||||
return nil, false, fmt.Errorf("failed to download %s: status code %d", url, resp.StatusCode)
|
return nil, false, fmt.Errorf("failed to download %s: status code %d", url, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it doesn't end with .sh, check for redirect
|
|
||||||
fmt.Printf("URL %s does not end with .sh, checking for redirect...\n", url)
|
fmt.Printf("URL %s does not end with .sh, checking for redirect...\n", url)
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
@ -48,7 +48,7 @@ func isValidURLAndFetchContent(url string, redirectCount int) ([]byte, bool, err
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Get(url) // Using Get to be able to read body if needed, though Head is often used for just status/headers
|
resp, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("failed to request %s: %w", url, err)
|
return nil, false, fmt.Errorf("failed to request %s: %w", url, err)
|
||||||
}
|
}
|
||||||
@ -63,7 +63,6 @@ func isValidURLAndFetchContent(url string, redirectCount int) ([]byte, bool, err
|
|||||||
fmt.Printf("Redirected to: %s\n", location.String())
|
fmt.Printf("Redirected to: %s\n", location.String())
|
||||||
return isValidURLAndFetchContent(location.String(), redirectCount+1)
|
return isValidURLAndFetchContent(location.String(), redirectCount+1)
|
||||||
default:
|
default:
|
||||||
// Not a .sh and not a redirect status code
|
|
||||||
return nil, false, fmt.Errorf("URL %s does not end in .sh and did not redirect (status: %d)", url, resp.StatusCode)
|
return nil, false, fmt.Errorf("URL %s does not end in .sh and did not redirect (status: %d)", url, resp.StatusCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,6 +72,48 @@ func hasShebang(content []byte) bool {
|
|||||||
return len(content) >= 2 && content[0] == '#' && content[1] == '!'
|
return len(content) >= 2 && content[0] == '#' && content[1] == '!'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// executeScript saves the script content to a temporary file and executes it.
|
||||||
|
func executeScript(content []byte) error {
|
||||||
|
tmpFile, err := os.CreateTemp("", "script-*.sh")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create temporary file: %w", err)
|
||||||
|
}
|
||||||
|
scriptPath := tmpFile.Name()
|
||||||
|
defer os.Remove(scriptPath) // Clean up the temp file
|
||||||
|
|
||||||
|
if _, err := tmpFile.Write(content); err != nil {
|
||||||
|
tmpFile.Close() // Close before attempting remove on error
|
||||||
|
return fmt.Errorf("failed to write to temporary file: %w", err)
|
||||||
|
}
|
||||||
|
if err := tmpFile.Close(); err != nil {
|
||||||
|
return fmt.Errorf("failed to close temporary file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the script executable
|
||||||
|
if err := os.Chmod(scriptPath, 0700); err != nil {
|
||||||
|
return fmt.Errorf("failed to make script executable: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n--- EXECUTING SCRIPT (%s) ---\n", scriptPath)
|
||||||
|
// The command to execute. On Unix-like systems, the kernel will use the shebang.
|
||||||
|
// For Windows, or if a specific interpreter is needed universally,
|
||||||
|
// one might need to parse the shebang and prepend the interpreter.
|
||||||
|
cmd := exec.Command(scriptPath)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = os.Stdin // Allow script to read from stdin
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
fmt.Println("\n--- EXECUTION FINISHED ---")
|
||||||
|
if err != nil {
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
return fmt.Errorf("script execution failed with exit code %d", exitErr.ExitCode())
|
||||||
|
}
|
||||||
|
return fmt.Errorf("script execution failed: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
fmt.Println("Usage: go run script_downloader.go <URL>")
|
fmt.Println("Usage: go run script_downloader.go <URL>")
|
||||||
@ -92,13 +133,78 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasShebang(content) {
|
if !hasShebang(content) {
|
||||||
fmt.Println("Valid script found. Content:")
|
fmt.Fprintf(os.Stderr, "Error: The file from URL does not start with a shebang '#!'.\n")
|
||||||
fmt.Println("--- SCRIPT START ---")
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Valid script found.")
|
||||||
|
|
||||||
|
var choice string
|
||||||
|
scriptActionForm := huh.NewForm(
|
||||||
|
huh.NewGroup(
|
||||||
|
huh.NewSelect[string]().
|
||||||
|
Title("What would you like to do with the script?").
|
||||||
|
Options(
|
||||||
|
huh.NewOption("Read the script", "read"),
|
||||||
|
huh.NewOption("Execute the script (Potentially DANGEROUS!)", "execute"),
|
||||||
|
huh.NewOption("Exit", "exit"),
|
||||||
|
).
|
||||||
|
Value(&choice),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
err = scriptActionForm.Run()
|
||||||
|
if err != nil {
|
||||||
|
// This can happen if the user cancels the form (e.g., Ctrl+C)
|
||||||
|
if err == huh.ErrUserAborted {
|
||||||
|
fmt.Println("Operation cancelled by user. Exiting.")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "Error running selection form: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch choice {
|
||||||
|
case "read":
|
||||||
|
fmt.Println("\n--- SCRIPT CONTENT START ---")
|
||||||
fmt.Print(string(content))
|
fmt.Print(string(content))
|
||||||
fmt.Println("--- SCRIPT END ---")
|
fmt.Println("--- SCRIPT CONTENT END ---")
|
||||||
} else {
|
case "execute":
|
||||||
fmt.Fprintf(os.Stderr, "Error: The file from URL %s does not start with a shebang '#!'.\n", initialURL)
|
fmt.Println("Attempting to execute the script...")
|
||||||
|
// Add an additional confirmation for execution due to security risks
|
||||||
|
var confirmExecute bool
|
||||||
|
confirmForm := huh.NewForm(
|
||||||
|
huh.NewGroup(
|
||||||
|
huh.NewConfirm().
|
||||||
|
Title("DANGER: You are about to execute a script from the internet.").
|
||||||
|
Description("Only proceed if you FULLY TRUST the source of this script.\nAre you sure you want to execute it?").
|
||||||
|
Affirmative("Yes, execute it!").
|
||||||
|
Negative("No, cancel.").
|
||||||
|
Value(&confirmExecute),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
err := confirmForm.Run()
|
||||||
|
if err != nil || !confirmExecute {
|
||||||
|
if err == huh.ErrUserAborted || !confirmExecute {
|
||||||
|
fmt.Println("Execution cancelled by user.")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error during confirmation: %v\n", err)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := executeScript(content); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error during script execution: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
case "exit":
|
||||||
|
fmt.Println("Exiting.")
|
||||||
|
os.Exit(0)
|
||||||
|
default:
|
||||||
|
// This path should ideally not be reached if huh.Select is used correctly
|
||||||
|
fmt.Fprintf(os.Stderr, "Invalid choice. Exiting.\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user