mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 09:53:03 +00:00
fixed mobile not running e2e tests and process update
This commit is contained in:
parent
2c374b2156
commit
c827651245
@ -4,9 +4,12 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
@ -14,15 +17,25 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Manager manages deployment processes via systemd
|
||||
// Manager manages deployment processes via systemd (Linux) or direct process spawning (macOS/other)
|
||||
type Manager struct {
|
||||
logger *zap.Logger
|
||||
logger *zap.Logger
|
||||
useSystemd bool
|
||||
|
||||
// For non-systemd mode: track running processes
|
||||
processes map[string]*exec.Cmd
|
||||
processesMu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewManager creates a new process manager
|
||||
func NewManager(logger *zap.Logger) *Manager {
|
||||
// Use systemd only on Linux
|
||||
useSystemd := runtime.GOOS == "linux"
|
||||
|
||||
return &Manager{
|
||||
logger: logger,
|
||||
logger: logger,
|
||||
useSystemd: useSystemd,
|
||||
processes: make(map[string]*exec.Cmd),
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,8 +47,13 @@ func (m *Manager) Start(ctx context.Context, deployment *deployments.Deployment,
|
||||
zap.String("deployment", deployment.Name),
|
||||
zap.String("namespace", deployment.Namespace),
|
||||
zap.String("service", serviceName),
|
||||
zap.Bool("systemd", m.useSystemd),
|
||||
)
|
||||
|
||||
if !m.useSystemd {
|
||||
return m.startDirect(ctx, deployment, workDir)
|
||||
}
|
||||
|
||||
// Create systemd service file
|
||||
if err := m.createSystemdService(deployment, workDir); err != nil {
|
||||
return fmt.Errorf("failed to create systemd service: %w", err)
|
||||
@ -64,6 +82,78 @@ func (m *Manager) Start(ctx context.Context, deployment *deployments.Deployment,
|
||||
return nil
|
||||
}
|
||||
|
||||
// startDirect starts a process directly without systemd (for macOS/local dev)
|
||||
func (m *Manager) startDirect(ctx context.Context, deployment *deployments.Deployment, workDir string) error {
|
||||
serviceName := m.getServiceName(deployment)
|
||||
startCmd := m.getStartCommand(deployment, workDir)
|
||||
|
||||
// Parse command
|
||||
parts := strings.Fields(startCmd)
|
||||
if len(parts) == 0 {
|
||||
return fmt.Errorf("empty start command")
|
||||
}
|
||||
|
||||
cmd := exec.Command(parts[0], parts[1:]...)
|
||||
cmd.Dir = workDir
|
||||
|
||||
// Set environment
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("PORT=%d", deployment.Port))
|
||||
for key, value := range deployment.Environment {
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value))
|
||||
}
|
||||
|
||||
// Create log file for output
|
||||
logDir := filepath.Join(os.Getenv("HOME"), ".orama", "logs", "deployments")
|
||||
os.MkdirAll(logDir, 0755)
|
||||
logFile, err := os.OpenFile(
|
||||
filepath.Join(logDir, serviceName+".log"),
|
||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND,
|
||||
0644,
|
||||
)
|
||||
if err != nil {
|
||||
m.logger.Warn("Failed to create log file", zap.Error(err))
|
||||
} else {
|
||||
cmd.Stdout = logFile
|
||||
cmd.Stderr = logFile
|
||||
}
|
||||
|
||||
// Start process
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start process: %w", err)
|
||||
}
|
||||
|
||||
// Track process
|
||||
m.processesMu.Lock()
|
||||
m.processes[serviceName] = cmd
|
||||
m.processesMu.Unlock()
|
||||
|
||||
// Monitor process in background
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
m.processesMu.Lock()
|
||||
delete(m.processes, serviceName)
|
||||
m.processesMu.Unlock()
|
||||
if err != nil {
|
||||
m.logger.Warn("Process exited with error",
|
||||
zap.String("service", serviceName),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
if logFile != nil {
|
||||
logFile.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
m.logger.Info("Deployment process started (direct)",
|
||||
zap.String("deployment", deployment.Name),
|
||||
zap.String("service", serviceName),
|
||||
zap.Int("pid", cmd.Process.Pid),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops a deployment process
|
||||
func (m *Manager) Stop(ctx context.Context, deployment *deployments.Deployment) error {
|
||||
serviceName := m.getServiceName(deployment)
|
||||
@ -73,6 +163,10 @@ func (m *Manager) Stop(ctx context.Context, deployment *deployments.Deployment)
|
||||
zap.String("service", serviceName),
|
||||
)
|
||||
|
||||
if !m.useSystemd {
|
||||
return m.stopDirect(serviceName)
|
||||
}
|
||||
|
||||
// Stop service
|
||||
if err := m.systemdStop(serviceName); err != nil {
|
||||
m.logger.Warn("Failed to stop service", zap.Error(err))
|
||||
@ -96,6 +190,25 @@ func (m *Manager) Stop(ctx context.Context, deployment *deployments.Deployment)
|
||||
return nil
|
||||
}
|
||||
|
||||
// stopDirect stops a directly spawned process
|
||||
func (m *Manager) stopDirect(serviceName string) error {
|
||||
m.processesMu.Lock()
|
||||
cmd, exists := m.processes[serviceName]
|
||||
m.processesMu.Unlock()
|
||||
|
||||
if !exists || cmd.Process == nil {
|
||||
return nil // Already stopped
|
||||
}
|
||||
|
||||
// Send SIGTERM
|
||||
if err := cmd.Process.Signal(os.Interrupt); err != nil {
|
||||
// Try SIGKILL if SIGTERM fails
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restart restarts a deployment process
|
||||
func (m *Manager) Restart(ctx context.Context, deployment *deployments.Deployment) error {
|
||||
serviceName := m.getServiceName(deployment)
|
||||
@ -105,6 +218,15 @@ func (m *Manager) Restart(ctx context.Context, deployment *deployments.Deploymen
|
||||
zap.String("service", serviceName),
|
||||
)
|
||||
|
||||
if !m.useSystemd {
|
||||
// For direct mode, stop and start
|
||||
m.stopDirect(serviceName)
|
||||
// Note: Would need workDir to restart, which we don't have here
|
||||
// For now, just log a warning
|
||||
m.logger.Warn("Restart not fully supported in direct mode")
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.systemdRestart(serviceName)
|
||||
}
|
||||
|
||||
@ -112,6 +234,16 @@ func (m *Manager) Restart(ctx context.Context, deployment *deployments.Deploymen
|
||||
func (m *Manager) Status(ctx context.Context, deployment *deployments.Deployment) (string, error) {
|
||||
serviceName := m.getServiceName(deployment)
|
||||
|
||||
if !m.useSystemd {
|
||||
m.processesMu.RLock()
|
||||
_, exists := m.processes[serviceName]
|
||||
m.processesMu.RUnlock()
|
||||
if exists {
|
||||
return "active", nil
|
||||
}
|
||||
return "inactive", nil
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "systemctl", "is-active", serviceName)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
@ -125,6 +257,24 @@ func (m *Manager) Status(ctx context.Context, deployment *deployments.Deployment
|
||||
func (m *Manager) GetLogs(ctx context.Context, deployment *deployments.Deployment, lines int, follow bool) ([]byte, error) {
|
||||
serviceName := m.getServiceName(deployment)
|
||||
|
||||
if !m.useSystemd {
|
||||
// Read from log file in direct mode
|
||||
logFile := filepath.Join(os.Getenv("HOME"), ".orama", "logs", "deployments", serviceName+".log")
|
||||
data, err := os.ReadFile(logFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read log file: %w", err)
|
||||
}
|
||||
// Return last N lines if specified
|
||||
if lines > 0 {
|
||||
logLines := strings.Split(string(data), "\n")
|
||||
if len(logLines) > lines {
|
||||
logLines = logLines[len(logLines)-lines:]
|
||||
}
|
||||
return []byte(strings.Join(logLines, "\n")), nil
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
args := []string{"-u", serviceName, "--no-pager"}
|
||||
if lines > 0 {
|
||||
args = append(args, "-n", fmt.Sprintf("%d", lines))
|
||||
@ -235,19 +385,27 @@ WantedBy=multi-user.target
|
||||
|
||||
// getStartCommand determines the start command for a deployment
|
||||
func (m *Manager) getStartCommand(deployment *deployments.Deployment, workDir string) string {
|
||||
// For systemd (Linux), use full paths. For direct mode, use PATH resolution.
|
||||
nodePath := "node"
|
||||
npmPath := "npm"
|
||||
if m.useSystemd {
|
||||
nodePath = "/usr/bin/node"
|
||||
npmPath = "/usr/bin/npm"
|
||||
}
|
||||
|
||||
switch deployment.Type {
|
||||
case deployments.DeploymentTypeNextJS:
|
||||
// Next.js standalone output places server at .next/standalone/server.js
|
||||
return "/usr/bin/node .next/standalone/server.js"
|
||||
return nodePath + " .next/standalone/server.js"
|
||||
case deployments.DeploymentTypeNodeJSBackend:
|
||||
// Check if ENTRY_POINT is set in environment
|
||||
if entryPoint, ok := deployment.Environment["ENTRY_POINT"]; ok {
|
||||
if entryPoint == "npm:start" {
|
||||
return "/usr/bin/npm start"
|
||||
return npmPath + " start"
|
||||
}
|
||||
return "/usr/bin/node " + entryPoint
|
||||
return nodePath + " " + entryPoint
|
||||
}
|
||||
return "/usr/bin/node index.js"
|
||||
return nodePath + " index.js"
|
||||
case deployments.DeploymentTypeGoBackend:
|
||||
return filepath.Join(workDir, "app")
|
||||
default:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user