mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
Deep audit changes: - Replace stub signature aggregator with real SDK warp implementation - Remove dead pkg/lpm package (SDK lpmintegration used instead) - Fix fs imports: use local utils.FileExists, DirExists, ExpandHome - Fix secp256k1fx imports: use node/vms/secp256k1fx - Fix platformvm imports: use node/vms/platformvm - Fix e2e test TODOs: uncomment BootstrapValidators assertions - Improve localnet stub implementations with real logic - Fix node.go fs.FileExists reference Note: Build requires GOWORK=off due to workspace dependency issues
195 lines
5.2 KiB
Go
195 lines
5.2 KiB
Go
// Copyright (C) 2022-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/luxfi/constants"
|
|
luxlog "github.com/luxfi/log"
|
|
|
|
"go.uber.org/zap"
|
|
"golang.org/x/mod/modfile"
|
|
)
|
|
|
|
func NonEmptyDirectory(dirName string) (bool, error) {
|
|
if !DirExists(dirName) {
|
|
return false, fmt.Errorf("%s is not a directory", dirName)
|
|
}
|
|
files, err := os.ReadDir(dirName)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return len(files) != 0, nil
|
|
}
|
|
|
|
// DirExists checks if a directory exists.
|
|
func DirExists(dirName string) bool {
|
|
info, err := os.Stat(dirName)
|
|
if os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
return info.IsDir()
|
|
}
|
|
|
|
// FileExists checks if a file exists.
|
|
func FileExists(filename string) bool {
|
|
info, err := os.Stat(filename)
|
|
if os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
return !info.IsDir()
|
|
}
|
|
|
|
// IsExecutable checks if a file is executable.
|
|
func IsExecutable(filename string) bool {
|
|
if !FileExists(filename) {
|
|
return false
|
|
}
|
|
info, _ := os.Stat(filename)
|
|
// A file perm can be seen as a 9-bit sequence mapping to: rwxrwxrwx
|
|
// read-write-execute for owner, group and everybody.
|
|
// 0o100 is the bit mask that, when applied with the bitwise-AND operator &,
|
|
// results in != 0 state whenever the file can be executed by the owner.
|
|
return info.Mode()&0o100 != 0
|
|
}
|
|
|
|
// UserHomePath returns the absolute path of a file located in the user's home directory.
|
|
func UserHomePath(filePath ...string) string {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return filepath.Join(filePath...)
|
|
}
|
|
fullPath := append([]string{home}, filePath...)
|
|
return filepath.Join(fullPath...)
|
|
}
|
|
|
|
// ExpandHome expands ~ symbol to home directory
|
|
func ExpandHome(path string) string {
|
|
if path == "" {
|
|
home, _ := os.UserHomeDir()
|
|
return home
|
|
}
|
|
if len(path) > 0 && path[0] == '~' {
|
|
home, _ := os.UserHomeDir()
|
|
path = filepath.Join(home, path[1:])
|
|
}
|
|
path, _ = filepath.Abs(path)
|
|
return path
|
|
}
|
|
|
|
// ReplaceUserHomeWithTilde replaces user home directory with ~
|
|
func ReplaceUserHomeWithTilde(path string) string {
|
|
home, _ := os.UserHomeDir()
|
|
if strings.HasPrefix(path, home) {
|
|
path = "~" + strings.TrimPrefix(path, home)
|
|
}
|
|
return path
|
|
}
|
|
|
|
// FileCopy copies a file from src to dst.
|
|
func FileCopy(src string, dst string) error {
|
|
if !FileExists(src) {
|
|
return fmt.Errorf("source file does not exist")
|
|
}
|
|
data, err := os.ReadFile(src) //nolint:gosec // G304: Copying file from validated path
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(dst, data, constants.WriteReadReadPerms)
|
|
}
|
|
|
|
// SetupExecFile copies a file into destination and set it to have exec perms,
|
|
// if destination either does not exists, or is not executable
|
|
func SetupExecFile(
|
|
log luxlog.Logger,
|
|
src string,
|
|
dst string,
|
|
) error {
|
|
if !IsExecutable(dst) {
|
|
if FileExists(dst) {
|
|
log.Error(
|
|
"binary was not properly installed on a previous CLI execution",
|
|
zap.String("binary-path", dst),
|
|
)
|
|
}
|
|
// Either it was never installed, or it was partially done (copy or chmod
|
|
// failure)
|
|
// As the file is not executable, there is no risk of encountering text file busy
|
|
// error during copy, because that happens when the binary is being executed.
|
|
if err := FileCopy(src, dst); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Chmod(dst, constants.DefaultPerms755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ReadFile reads a file and returns the contents as a string
|
|
func ReadFile(filePath string) (string, error) {
|
|
filePath = ExpandHome(filePath)
|
|
if !FileExists(filePath) {
|
|
return "", fmt.Errorf("file does not exist")
|
|
} else {
|
|
data, err := os.ReadFile(filePath) //nolint:gosec // G304: Reading file from validated path
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(data), nil
|
|
}
|
|
}
|
|
|
|
// WriteStringToFile writes a string to a file
|
|
func WriteStringToFile(filePath string, data string) error {
|
|
filePath = ExpandHome(filePath)
|
|
return os.WriteFile(filePath, []byte(data), constants.WriteReadReadPerms)
|
|
}
|
|
|
|
// SizeInKB returns the size of a file or directory.
|
|
func SizeInKB(path string) (int64, error) {
|
|
var size int64
|
|
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() {
|
|
size += info.Size()
|
|
}
|
|
return err
|
|
})
|
|
return size, err
|
|
}
|
|
|
|
// RemoteComposeFile returns the path to the remote docker-compose file
|
|
func GetRemoteComposeFile() string {
|
|
return filepath.Join(constants.CloudNodeCLIConfigBasePath, "services", "docker-compose.yml")
|
|
}
|
|
|
|
// GetRemoteComposeServicePath returns the path to the remote service directory
|
|
func GetRemoteComposeServicePath(serviceName string, dirs ...string) string {
|
|
servicePrefix := filepath.Join(constants.CloudNodeCLIConfigBasePath, "services", serviceName)
|
|
return filepath.Join(append([]string{servicePrefix}, dirs...)...)
|
|
}
|
|
|
|
// ReadGoVersion reads the Go version from the go.mod file
|
|
func ReadGoVersion(filePath string) (string, error) {
|
|
data, err := os.ReadFile(filePath) //nolint:gosec // G304: Reading go.mod file from provided path
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
modFile, err := modfile.Parse(filePath, data, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if modFile.Go != nil {
|
|
return modFile.Go.Version, nil
|
|
}
|
|
return "", fmt.Errorf("go version not found in %s", filePath)
|
|
}
|