oc-local/cmd/runtutils.go
2024-10-15 17:37:37 +02:00

253 lines
6.4 KiB
Go

package cmd
import (
"bufio"
"context"
"fmt"
"io"
"io/fs"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
)
var processes []*exec.Cmd
var mu sync.Mutex
// walkFolder starts processes for files starting with "az" in the given folder
func walkFolder(folderPath string, ctx context.Context, wg *sync.WaitGroup) {
os.Chdir(folderPath)
files, err := os.ReadDir(".")
if err != nil {
log.Fatal(err)
}
for _, file := range files {
// Check if filename starts with "oc-"
if strings.HasPrefix(file.Name(), "oc-") && (!strings.Contains(file.Name(), ".") || strings.Contains(file.Name(), ".exe")) {
// Prepare the command to start the process
wg.Add(1)
go startProcess(ctx, file.Name(), wg)
}
// find
if strings.HasPrefix(strings.ToLower(file.Name()), "caddy") && strings.ToLower(file.Name()) != "caddyfile" {
runtime := findCaddyRuntime(".")
if runtime != "" {
// Prepare the command to start the process
wg.Add(1)
go startProcess(ctx, "./"+runtime, wg, "run")
}
}
}
}
func startProcess(ctx context.Context, program string, wg *sync.WaitGroup, args ...string) {
defer wg.Done()
// Create log file
logFile, err := os.Create(program + ".log")
if err != nil {
log.Printf("Failed to create log file: %v", err)
return
}
cmd := exec.CommandContext(ctx, program, args...)
mu.Lock()
processes = append(processes, cmd)
mu.Unlock()
// Create pipes to capture the output in real-time
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
log.Printf("Failed to get stdout pipe: %v", err)
return
}
stderrPipe, err := cmd.StderrPipe()
if err != nil {
log.Printf("Failed to get stderr pipe: %v", err)
return
}
// Start the command
if err := cmd.Start(); err != nil {
log.Printf("Failed to start process: %s", program)
return
}
log.Printf("Started process: %s", program)
// Create goroutines to read stdout and stderr concurrently
go func() {
scanner := bufio.NewScanner(stdoutPipe)
for scanner.Scan() {
fmt.Fprintln(logFile, scanner.Text()) // Print each line from stdout as it arrives
}
if err := scanner.Err(); err != nil {
log.Printf("Error reading stdout: %v", err)
}
}()
go func() {
scanner := bufio.NewScanner(stderrPipe)
for scanner.Scan() {
fmt.Fprintln(logFile, scanner.Text()) // Print each line from stderr as it arrives
}
if err := scanner.Err(); err != nil {
log.Printf("Error reading stderr: %v", err)
}
}()
// Wait for the process to finish
if err := cmd.Wait(); err != nil {
log.Printf("Process finished with error: %v", err)
} else {
log.Printf("Process finished successfully: %s", program)
}
}
// stopAllProcesses stops all the started processes
func stopAllProcesses() {
mu.Lock()
defer mu.Unlock()
for _, cmd := range processes {
if cmd.Process != nil {
log.Printf("Killing process with PID %d", cmd.Process.Pid)
err := cmd.Process.Kill()
if err != nil {
log.Printf("Failed to kill process with PID %d: %v", cmd.Process.Pid, err)
}
}
}
processes = []*exec.Cmd{}
}
func findCaddyRuntime(folder string) string {
var foundFile string
err := filepath.WalkDir(folder, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
// Check if it's a file (not a directory)
if !d.IsDir() {
filename := d.Name()
// Check if the filename starts with "caddy" (case-insensitive) and is not "CaddyFile"
if strings.HasPrefix(strings.ToLower(filename), "caddy") && strings.ToLower(filename) != "caddyfile" {
foundFile = filename
return filepath.SkipDir // Skip further traversal once we find a match
}
}
return nil
})
if err != nil {
fmt.Println("Error:", err)
return ""
}
return foundFile
}
func downloadLatestCaddy(outputFolder string) error {
// Define the GitHub API URL for the latest Caddy release
apiURL := "https://api.github.com/repos/caddyserver/caddy/releases/latest"
// Get the system's OS and architecture
osName := runtime.GOOS
archName := runtime.GOARCH
// Make an HTTP GET request to the GitHub API
resp, err := http.Get(apiURL)
if err != nil {
return fmt.Errorf("failed to get latest Caddy release: %v", err)
}
defer resp.Body.Close()
// Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read GitHub API response: %v", err)
}
// Find the download URL for the current platform (OS + architecture)
downloadURL := ""
urlPrefix := fmt.Sprintf("%s_%s", osName, archName)
lines := strings.Split(string(body), "\n")
for _, line := range lines {
if strings.Contains(line, "browser_download_url") && strings.Contains(line, urlPrefix) {
// Extract the URL
parts := strings.Split(line, "\"")
for _, part := range parts {
if strings.HasPrefix(part, "https://") && strings.Contains(part, urlPrefix) && (strings.HasSuffix(part, ".tar.gz") || strings.HasSuffix(part, ".zip")) {
downloadURL = part
break
}
}
if downloadURL != "" {
break
}
}
}
if downloadURL == "" {
return fmt.Errorf("failed to find a suitable Caddy binary for %s_%s", osName, archName)
}
// Make an HTTP GET request to download the file
resp, err = http.Get(downloadURL)
if err != nil {
return fmt.Errorf("failed to download Caddy executable: %v", err)
}
defer resp.Body.Close()
suffix := ""
if strings.Contains(downloadURL, ".tar.gz") {
suffix = ".tar.gz"
} else if strings.Contains(downloadURL, ".zip") {
suffix = ".zip"
}
// Create the output file
outputPath := fmt.Sprintf("%s/caddy"+suffix, strings.TrimRight(outputFolder, "/"))
outFile, err := os.Create(outputPath)
if err != nil {
return fmt.Errorf("failed to create output file: %v", err)
}
defer outFile.Close()
// Write the response body to the output file
_, err = io.Copy(outFile, resp.Body)
if err != nil {
return fmt.Errorf("failed to write to output file: %v", err)
}
// Extract the downloaded file
if strings.Contains(downloadURL, ".tar.gz") {
err = exec.Command("tar", "-xzf", outputPath, "-C", outputFolder).Run()
if err != nil {
return fmt.Errorf("failed to extract tar.gz file: %v", err)
}
} else if strings.Contains(downloadURL, ".zip") {
err = exec.Command("unzip", outputPath, "-d", outputFolder).Run()
if err != nil {
return fmt.Errorf("failed to extract zip file: %v", err)
}
}
// Remove the downloaded file
err = os.Remove(outputPath)
if err != nil {
return fmt.Errorf("failed to remove: %v", err)
}
fmt.Printf("Caddy downloaded successfully to %s\n", outputPath)
return nil
}