mirror of
https://github.com/fleetdm/fleet
synced 2026-05-22 08:28:52 +00:00
169 lines
4.6 KiB
Go
169 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func main() {
|
|
// Ensure there's a make target specified.
|
|
if len(os.Args) < 2 {
|
|
os.Args = append(os.Args, "help")
|
|
}
|
|
|
|
// Determine the path to the top-level directory (where the Makefile resides).
|
|
repoRoot, err := getRepoRoot()
|
|
if err != nil {
|
|
fmt.Printf("Error determining repo root: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Change the working directory to the repo root.
|
|
if err := os.Chdir(repoRoot); err != nil {
|
|
fmt.Printf("Error changing directory to repo root: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Extract the make target.
|
|
makeTarget := os.Args[1]
|
|
|
|
// Split arguments into options and make arguments.
|
|
options, makeArgs := splitArgs(os.Args[2:])
|
|
|
|
// Special logic for the help command.
|
|
if makeTarget == "help" {
|
|
if len(os.Args) > 2 && !strings.HasPrefix(os.Args[2], "--") {
|
|
options["REFORMAT_OPTIONS"] = "true"
|
|
} else {
|
|
fmt.Println("\033[1mNAME\033[0m")
|
|
fmt.Println(" fdm - developer tools for fleet device management")
|
|
fmt.Println()
|
|
fmt.Println("\033[1mUSAGE:\033[0m")
|
|
fmt.Println(" fdm <command> [--option=value ...] -- [make-options]")
|
|
fmt.Println()
|
|
fmt.Println("\033[1mCOMMANDS:\033[0m")
|
|
options["HELP_CMD_PREFIX"] = "fdm"
|
|
}
|
|
}
|
|
|
|
// Transform options into Makefile-compatible variables.
|
|
makeVars := transformToMakeVars(options)
|
|
makeVars = append(makeVars, "TOOL_CMD=fdm")
|
|
|
|
// Call the Makefile with the specified target, Make variables, and additional arguments.
|
|
err = callMake(makeTarget, makeVars, makeArgs)
|
|
if err != nil {
|
|
fmt.Printf("Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// splitArgs splits the arguments into options and make arguments based on the `--` delimiter.
|
|
func splitArgs(args []string) (map[string]string, []string) {
|
|
options := make(map[string]string)
|
|
var makeArgs []string
|
|
cliArgs := " "
|
|
positionalArgsIndex := 1
|
|
isMakeArgs := false
|
|
skipNext := false
|
|
|
|
for idx, arg := range args {
|
|
if skipNext {
|
|
skipNext = false
|
|
continue
|
|
}
|
|
|
|
if arg == "--" {
|
|
isMakeArgs = true
|
|
continue
|
|
}
|
|
|
|
switch {
|
|
// If we're processing make args (anything after a bare -- )
|
|
// then add the current arg to the list.
|
|
case isMakeArgs:
|
|
makeArgs = append(makeArgs, arg)
|
|
// Otherwise if the arg has a -- prefix, treat it like an option
|
|
// for the command.
|
|
case strings.HasPrefix(arg, "--"):
|
|
// Remove "--" and split by "=".
|
|
parts := strings.SplitN(arg[2:], "=", 2)
|
|
switch {
|
|
// Handle options like --name=foo
|
|
case len(parts) == 2:
|
|
options[parts[0]] = parts[1]
|
|
cliArgs += arg + " "
|
|
// Handle options like --name foo
|
|
case idx+1 < len(args) && !strings.HasPrefix(args[idx+1], "--"):
|
|
options[arg[2:]] = args[idx+1]
|
|
cliArgs += arg + " " + args[idx+1] + " "
|
|
skipNext = true
|
|
// Handle options like --useturbocharge by assuming they're booleans.
|
|
default:
|
|
options[parts[0]] = "true"
|
|
cliArgs += arg + " "
|
|
}
|
|
// Otherwise assume we're dealing with a positional argument.
|
|
default:
|
|
options["arg"+strconv.Itoa(positionalArgsIndex)] = arg
|
|
positionalArgsIndex++
|
|
}
|
|
}
|
|
options["CLI_ARGS"] = cliArgs
|
|
return options, makeArgs
|
|
}
|
|
|
|
// transformToMakeVars converts kebab-cased options to snake-cased Makefile variables.
|
|
func transformToMakeVars(options map[string]string) []string {
|
|
var makeVars []string
|
|
|
|
for key, value := range options {
|
|
// Convert kebab-case to snake_case and uppercase.
|
|
varName := strings.ToUpper(strings.ReplaceAll(key, "-", "_"))
|
|
makeVars = append(makeVars, fmt.Sprintf("%s=%s", varName, value))
|
|
}
|
|
|
|
return makeVars
|
|
}
|
|
|
|
// callMake invokes the `make` command with the given target, variables, and additional arguments.
|
|
func callMake(target string, makeVars []string, makeArgs []string) error {
|
|
// Construct the command with target and makeArgs.
|
|
finalArgs := []string{target}
|
|
finalArgs = append(finalArgs, makeVars...)
|
|
finalArgs = append(finalArgs, makeArgs...)
|
|
cmd := exec.Command("make", finalArgs...)
|
|
|
|
// Use the same stdin, stdout, and stderr as the parent process.
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
// Run the command.
|
|
return cmd.Run()
|
|
}
|
|
|
|
// getRepoRoot determines the repo root (top-level directory) relative to this binary.
|
|
func getRepoRoot() (string, error) {
|
|
// Get the path of the currently executing binary
|
|
executable, err := os.Executable()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Get the path of the binary, following symlinks.
|
|
execDir, err := filepath.EvalSymlinks(executable)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// Get the directory.
|
|
execDir = filepath.Dir(execDir)
|
|
|
|
// Compute the repo root relative to the binary's location.
|
|
repoRoot := filepath.Join(execDir, "../")
|
|
return filepath.Abs(repoRoot)
|
|
}
|