cmd/hub/ext/run.go (118 lines of code) (raw):
// Copyright (c) 2022 EPAM Systems, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package ext
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/epam/hubctl/cmd/hub/config"
"github.com/epam/hubctl/cmd/hub/util"
)
const hubDir = ".hub"
func ExtensionPath(what, args []string) (string, []string, error) {
searchDirs := GetExtensionLocations()
for i := len(what); i > 0; i-- {
script := "hub-" + strings.Join(what[0:i], "-")
newArgs := append(what[i:], args...)
if config.Trace {
log.Printf("Trying %s with args %v", script, newArgs)
}
for _, dir := range searchDirs {
path := filepath.Join(dir, script)
_, err := os.Stat(path)
if err != nil {
if util.NoSuchFile(err) {
continue
}
util.Warn("Unable to stat `%s`: %v", path, err)
} else {
// TODO check file mode
// TODO allow extension placement in a dedicated subdirectory
return path, newArgs, nil
}
}
path, err := exec.LookPath(script)
if err == nil {
return path, newArgs, nil
}
}
customHubDir := os.Getenv("HUB_EXTENSIONS")
printCustomHubDir := ""
if customHubDir != "" {
printCustomHubDir = fmt.Sprintf(", $HUB_EXTENSIONS=%s", customHubDir)
}
home := os.Getenv("HOME")
homeHubDir := ""
if home != "" {
homeHubDir = filepath.Join(home, hubDir)
} else {
if config.Verbose {
util.Warn("Unable to lookup $HOME: no home directory set in OS environment")
}
}
maybeInstall := ""
if customHubDir == "" && homeHubDir != "" {
_, err := os.Stat(homeHubDir)
verb := "update"
if err != nil {
if util.NoSuchFile(err) {
verb = "install"
}
}
maybeInstall = fmt.Sprintf("\n\t%s Hub CTL extensions with `hub extensions %[1]s`?", verb)
}
return "", nil, fmt.Errorf("Extension not found in %v%s, nor $PATH%s", searchDirs, printCustomHubDir, maybeInstall)
}
func GetExtensionLocations() []string {
results := []string{filepath.Join(".", hubDir)}
customHubDir := os.Getenv("HUB_EXTENSIONS")
if customHubDir != "" {
results = append(results, customHubDir)
}
if os.Getenv("HOME") != "" {
home := os.Getenv("HOME")
hubHome := filepath.Join(home, hubDir)
_, err := os.Stat(hubHome)
if err == nil {
results = append(results, hubHome)
}
}
return append(results, "/usr/local/share/hub", "/usr/share/hub")
}
func RunExtension(what, args []string) {
code, err := runExtension(what, args)
if err != nil {
log.Fatalf("Unable to call %v extension: %v", what, err)
}
os.Exit(code)
}
func runExtension(what, args []string) (int, error) {
if config.Debug {
log.Printf("Searching extension %v with args %v", what, args)
}
executable, args, err := ExtensionPath(what, args)
if err != nil {
return 0, err
}
if config.Debug {
log.Printf("Found extension %s %v", executable, args)
}
cmd := exec.Cmd{
Path: executable,
Args: append([]string{filepath.Base(executable)}, args...),
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
err = cmd.Run()
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
return exitError.ExitCode(), nil
}
code := 0
if ps := cmd.ProcessState; ps != nil {
code = ps.ExitCode()
}
return code, err
}
return 0, nil
}