Updated installation process simplified it

This commit is contained in:
anonpenguin23 2026-02-14 14:06:14 +02:00
parent 749d5ed5e7
commit ba4e2688e4
10 changed files with 124 additions and 298 deletions

View File

@ -94,6 +94,8 @@ build-linux: deps
GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX)" -trimpath -o bin-linux/orama-cli ./cmd/cli GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS_LINUX)" -trimpath -o bin-linux/orama-cli ./cmd/cli
@echo "Building Olric for linux/amd64..." @echo "Building Olric for linux/amd64..."
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -trimpath -o bin-linux/olric-server github.com/olric-data/olric/cmd/olric-server GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -trimpath -o bin-linux/olric-server github.com/olric-data/olric/cmd/olric-server
@echo "Building IPFS Cluster Service for linux/amd64..."
GOOS=linux GOARCH=amd64 GOBIN=$(CURDIR)/bin-linux go install -ldflags "-s -w" -trimpath github.com/ipfs-cluster/ipfs-cluster/cmd/ipfs-cluster-service@latest
@echo "✓ All Linux binaries built in bin-linux/" @echo "✓ All Linux binaries built in bin-linux/"
@echo "" @echo ""
@echo "Next steps:" @echo "Next steps:"

View File

@ -14,7 +14,26 @@ func Handle(args []string) {
os.Exit(1) os.Exit(1)
} }
// Create orchestrator // Resolve base domain interactively if not provided (before local/VPS branch)
if flags.BaseDomain == "" {
flags.BaseDomain = promptForBaseDomain()
}
// Local mode: not running as root → orchestrate install via SSH
if os.Geteuid() != 0 {
remote, err := NewRemoteOrchestrator(flags)
if err != nil {
fmt.Fprintf(os.Stderr, "❌ %v\n", err)
os.Exit(1)
}
if err := remote.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "❌ %v\n", err)
os.Exit(1)
}
return
}
// VPS mode: running as root on the VPS — existing behavior
orchestrator, err := NewOrchestrator(flags) orchestrator, err := NewOrchestrator(flags)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "❌ %v\n", err) fmt.Fprintf(os.Stderr, "❌ %v\n", err)
@ -27,12 +46,6 @@ func Handle(args []string) {
os.Exit(1) os.Exit(1)
} }
// Check root privileges
if err := orchestrator.validator.ValidateRootPrivileges(); err != nil {
fmt.Fprintf(os.Stderr, "❌ %v\n", err)
os.Exit(1)
}
// Check port availability before proceeding // Check port availability before proceeding
if err := orchestrator.validator.ValidatePorts(); err != nil { if err := orchestrator.validator.ValidatePorts(); err != nil {
fmt.Fprintf(os.Stderr, "❌ %v\n", err) fmt.Fprintf(os.Stderr, "❌ %v\n", err)

View File

@ -8,15 +8,12 @@ import (
// Flags represents install command flags // Flags represents install command flags
type Flags struct { type Flags struct {
VpsIP string VpsIP string
Domain string Domain string
BaseDomain string // Base domain for deployment routing (e.g., "dbrs.space") BaseDomain string // Base domain for deployment routing (e.g., "dbrs.space")
Branch string Force bool
NoPull bool DryRun bool
Force bool SkipChecks bool
DryRun bool
SkipChecks bool
PreBuilt bool // Skip building binaries, use pre-built binaries already on disk
Nameserver bool // Make this node a nameserver (runs CoreDNS + Caddy) Nameserver bool // Make this node a nameserver (runs CoreDNS + Caddy)
JoinAddress string // HTTPS URL of existing node (e.g., https://node1.dbrs.space) JoinAddress string // HTTPS URL of existing node (e.g., https://node1.dbrs.space)
Token string // Invite token for joining (from orama invite) Token string // Invite token for joining (from orama invite)
@ -57,12 +54,9 @@ func ParseFlags(args []string) (*Flags, error) {
fs.StringVar(&flags.VpsIP, "vps-ip", "", "Public IP of this VPS (required)") fs.StringVar(&flags.VpsIP, "vps-ip", "", "Public IP of this VPS (required)")
fs.StringVar(&flags.Domain, "domain", "", "Domain name for HTTPS (optional, e.g. gateway.example.com)") fs.StringVar(&flags.Domain, "domain", "", "Domain name for HTTPS (optional, e.g. gateway.example.com)")
fs.StringVar(&flags.BaseDomain, "base-domain", "", "Base domain for deployment routing (e.g., dbrs.space)") fs.StringVar(&flags.BaseDomain, "base-domain", "", "Base domain for deployment routing (e.g., dbrs.space)")
fs.StringVar(&flags.Branch, "branch", "main", "Git branch to use (main or nightly)")
fs.BoolVar(&flags.NoPull, "no-pull", false, "Skip git clone/pull, use existing repository in /home/debros/src")
fs.BoolVar(&flags.Force, "force", false, "Force reconfiguration even if already installed") fs.BoolVar(&flags.Force, "force", false, "Force reconfiguration even if already installed")
fs.BoolVar(&flags.DryRun, "dry-run", false, "Show what would be done without making changes") fs.BoolVar(&flags.DryRun, "dry-run", false, "Show what would be done without making changes")
fs.BoolVar(&flags.SkipChecks, "skip-checks", false, "Skip minimum resource checks (RAM/CPU)") fs.BoolVar(&flags.SkipChecks, "skip-checks", false, "Skip minimum resource checks (RAM/CPU)")
fs.BoolVar(&flags.PreBuilt, "pre-built", false, "Skip building binaries on VPS, use pre-built binaries already in /home/debros/bin and /usr/local/bin")
fs.BoolVar(&flags.Nameserver, "nameserver", false, "Make this node a nameserver (runs CoreDNS + Caddy)") fs.BoolVar(&flags.Nameserver, "nameserver", false, "Make this node a nameserver (runs CoreDNS + Caddy)")
// Cluster join flags // Cluster join flags

View File

@ -33,18 +33,13 @@ func NewOrchestrator(flags *Flags) (*Orchestrator, error) {
oramaHome := "/home/debros" oramaHome := "/home/debros"
oramaDir := oramaHome + "/.orama" oramaDir := oramaHome + "/.orama"
// Prompt for base domain if not provided via flag
if flags.BaseDomain == "" {
flags.BaseDomain = promptForBaseDomain()
}
// Normalize peers // Normalize peers
peers, err := utils.NormalizePeers(flags.PeersStr) peers, err := utils.NormalizePeers(flags.PeersStr)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid peers: %w", err) return nil, fmt.Errorf("invalid peers: %w", err)
} }
setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.Branch, flags.NoPull, flags.SkipChecks, flags.PreBuilt) setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.SkipChecks)
setup.SetNameserver(flags.Nameserver) setup.SetNameserver(flags.Nameserver)
// Configure Anyone mode // Configure Anyone mode
@ -84,18 +79,6 @@ func NewOrchestrator(flags *Flags) (*Orchestrator, error) {
func (o *Orchestrator) Execute() error { func (o *Orchestrator) Execute() error {
fmt.Printf("🚀 Starting production installation...\n\n") fmt.Printf("🚀 Starting production installation...\n\n")
// Inform user if skipping git pull
if o.flags.NoPull {
fmt.Printf(" ⚠️ --no-pull flag enabled: Skipping git clone/pull\n")
fmt.Printf(" Using existing repository at /home/debros/src\n")
}
// Inform user if using pre-built binaries
if o.flags.PreBuilt {
fmt.Printf(" ⚠️ --pre-built flag enabled: Skipping all Go compilation\n")
fmt.Printf(" Using pre-built binaries from /home/debros/bin and /usr/local/bin\n")
}
// Validate DNS if domain is provided // Validate DNS if domain is provided
o.validator.ValidateDNS() o.validator.ValidateDNS()
@ -112,7 +95,7 @@ func (o *Orchestrator) Execute() error {
ORPort: o.flags.AnyoneORPort, ORPort: o.flags.AnyoneORPort,
} }
} }
utils.ShowDryRunSummaryWithRelay(o.flags.VpsIP, o.flags.Domain, o.flags.Branch, o.peers, o.flags.JoinAddress, o.validator.IsFirstNode(), o.oramaDir, relayInfo) utils.ShowDryRunSummaryWithRelay(o.flags.VpsIP, o.flags.Domain, "main", o.peers, o.flags.JoinAddress, o.validator.IsFirstNode(), o.oramaDir, relayInfo)
return nil return nil
} }
@ -131,7 +114,7 @@ func (o *Orchestrator) Execute() error {
anyoneORPort = 9001 anyoneORPort = 9001
} }
prefs := &production.NodePreferences{ prefs := &production.NodePreferences{
Branch: o.flags.Branch, Branch: "main",
Nameserver: o.flags.Nameserver, Nameserver: o.flags.Nameserver,
AnyoneClient: o.flags.AnyoneClient, AnyoneClient: o.flags.AnyoneClient,
AnyoneRelay: o.flags.AnyoneRelay, AnyoneRelay: o.flags.AnyoneRelay,

View File

@ -10,10 +10,7 @@ import (
type Flags struct { type Flags struct {
Force bool Force bool
RestartServices bool RestartServices bool
NoPull bool
PreBuilt bool
SkipChecks bool SkipChecks bool
Branch string
Nameserver *bool // Pointer so we can detect if explicitly set vs default Nameserver *bool // Pointer so we can detect if explicitly set vs default
// Anyone flags // Anyone flags
@ -39,10 +36,7 @@ func ParseFlags(args []string) (*Flags, error) {
fs.BoolVar(&flags.Force, "force", false, "Reconfigure all settings") fs.BoolVar(&flags.Force, "force", false, "Reconfigure all settings")
fs.BoolVar(&flags.RestartServices, "restart", false, "Automatically restart services after upgrade") fs.BoolVar(&flags.RestartServices, "restart", false, "Automatically restart services after upgrade")
fs.BoolVar(&flags.NoPull, "no-pull", false, "Skip source download, use existing /home/debros/src")
fs.BoolVar(&flags.PreBuilt, "pre-built", false, "Skip building binaries on VPS, use pre-built binaries already in /home/debros/bin and /usr/local/bin")
fs.BoolVar(&flags.SkipChecks, "skip-checks", false, "Skip minimum resource checks (RAM/CPU)") fs.BoolVar(&flags.SkipChecks, "skip-checks", false, "Skip minimum resource checks (RAM/CPU)")
fs.StringVar(&flags.Branch, "branch", "", "Git branch to use (uses saved preference if not specified)")
// Nameserver flag - use pointer to detect if explicitly set // Nameserver flag - use pointer to detect if explicitly set
nameserver := fs.Bool("nameserver", false, "Make this node a nameserver (uses saved preference if not specified)") nameserver := fs.Bool("nameserver", false, "Make this node a nameserver (uses saved preference if not specified)")

View File

@ -32,19 +32,13 @@ func NewOrchestrator(flags *Flags) *Orchestrator {
// Load existing preferences // Load existing preferences
prefs := production.LoadPreferences(oramaDir) prefs := production.LoadPreferences(oramaDir)
// Use saved branch if not specified
branch := flags.Branch
if branch == "" {
branch = prefs.Branch
}
// Use saved nameserver preference if not explicitly specified // Use saved nameserver preference if not explicitly specified
isNameserver := prefs.Nameserver isNameserver := prefs.Nameserver
if flags.Nameserver != nil { if flags.Nameserver != nil {
isNameserver = *flags.Nameserver isNameserver = *flags.Nameserver
} }
setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, branch, flags.NoPull, flags.SkipChecks, flags.PreBuilt) setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.SkipChecks)
setup.SetNameserver(isNameserver) setup.SetNameserver(isNameserver)
// Configure Anyone mode (flag > saved preference > auto-detect) // Configure Anyone mode (flag > saved preference > auto-detect)
@ -103,18 +97,6 @@ func (o *Orchestrator) Execute() error {
fmt.Printf(" This will preserve existing configurations and data\n") fmt.Printf(" This will preserve existing configurations and data\n")
fmt.Printf(" Configurations will be updated to latest format\n\n") fmt.Printf(" Configurations will be updated to latest format\n\n")
// Log if --no-pull is enabled
if o.flags.NoPull {
fmt.Printf(" ⚠️ --no-pull flag enabled: Skipping git clone/pull\n")
fmt.Printf(" Using existing repository at %s/src\n", o.oramaHome)
}
// Log if --pre-built is enabled
if o.flags.PreBuilt {
fmt.Printf(" ⚠️ --pre-built flag enabled: Skipping all Go compilation\n")
fmt.Printf(" Using pre-built binaries from %s/bin and /usr/local/bin\n", o.oramaHome)
}
// Handle branch preferences // Handle branch preferences
if err := o.handleBranchPreferences(); err != nil { if err := o.handleBranchPreferences(); err != nil {
return err return err
@ -216,15 +198,6 @@ func (o *Orchestrator) handleBranchPreferences() error {
prefs := production.LoadPreferences(o.oramaDir) prefs := production.LoadPreferences(o.oramaDir)
prefsChanged := false prefsChanged := false
// If branch was explicitly provided, update it
if o.flags.Branch != "" {
prefs.Branch = o.flags.Branch
prefsChanged = true
fmt.Printf(" Using branch: %s (saved for future upgrades)\n", o.flags.Branch)
} else {
fmt.Printf(" Using branch: %s (from saved preference)\n", prefs.Branch)
}
// If nameserver was explicitly provided, update it // If nameserver was explicitly provided, update it
if o.flags.Nameserver != nil { if o.flags.Nameserver != nil {
prefs.Nameserver = *o.flags.Nameserver prefs.Nameserver = *o.flags.Nameserver

View File

@ -116,66 +116,84 @@ func (ad *ArchitectureDetector) Detect() (string, error) {
} }
} }
// DependencyChecker validates external tool availability // DependencyChecker validates external tool availability and auto-installs missing ones
type DependencyChecker struct { type DependencyChecker struct{}
skipOptional bool
}
// NewDependencyChecker creates a new checker // NewDependencyChecker creates a new checker
func NewDependencyChecker(skipOptional bool) *DependencyChecker { func NewDependencyChecker(_ bool) *DependencyChecker {
return &DependencyChecker{ return &DependencyChecker{}
skipOptional: skipOptional,
}
} }
// Dependency represents an external binary dependency // Dependency represents an external binary dependency
type Dependency struct { type Dependency struct {
Name string Name string
Command string Command string
Optional bool AptPkg string // apt package name to install
InstallHint string
} }
// CheckAll validates all required dependencies // CheckAll validates all required dependencies, auto-installing any that are missing.
func (dc *DependencyChecker) CheckAll() ([]Dependency, error) { func (dc *DependencyChecker) CheckAll() ([]Dependency, error) {
dependencies := []Dependency{ dependencies := []Dependency{
{ {Name: "curl", Command: "curl", AptPkg: "curl"},
Name: "curl", {Name: "git", Command: "git", AptPkg: "git"},
Command: "curl", {Name: "make", Command: "make", AptPkg: "make"},
Optional: false, {Name: "jq", Command: "jq", AptPkg: "jq"},
InstallHint: "Usually pre-installed; if missing: apt-get install curl", {Name: "speedtest", Command: "speedtest-cli", AptPkg: "speedtest-cli"},
},
{
Name: "git",
Command: "git",
Optional: false,
InstallHint: "Install with: apt-get install git",
},
{
Name: "make",
Command: "make",
Optional: false,
InstallHint: "Install with: apt-get install make",
},
} }
var missing []Dependency var missing []Dependency
for _, dep := range dependencies { for _, dep := range dependencies {
if _, err := exec.LookPath(dep.Command); err != nil { if _, err := exec.LookPath(dep.Command); err != nil {
if !dep.Optional || !dc.skipOptional { missing = append(missing, dep)
missing = append(missing, dep)
}
} }
} }
if len(missing) > 0 { if len(missing) == 0 {
errMsg := "missing required dependencies:\n" return nil, nil
for _, dep := range missing {
errMsg += fmt.Sprintf(" - %s (%s): %s\n", dep.Name, dep.Command, dep.InstallHint)
}
return missing, fmt.Errorf("%s", errMsg)
} }
// Auto-install missing dependencies
var pkgs []string
var names []string
for _, dep := range missing {
pkgs = append(pkgs, dep.AptPkg)
names = append(names, dep.Name)
}
fmt.Fprintf(os.Stderr, " Installing missing dependencies: %s\n", strings.Join(names, ", "))
// apt-get update first
update := exec.Command("apt-get", "update", "-qq")
update.Stdout = os.Stdout
update.Stderr = os.Stderr
update.Run() // best-effort, don't fail on update
// apt-get install
args := append([]string{"install", "-y", "-qq"}, pkgs...)
install := exec.Command("apt-get", args...)
install.Stdout = os.Stdout
install.Stderr = os.Stderr
if err := install.Run(); err != nil {
return missing, fmt.Errorf("failed to install dependencies (%s): %w", strings.Join(names, ", "), err)
}
// Verify after install
var stillMissing []Dependency
for _, dep := range missing {
if _, err := exec.LookPath(dep.Command); err != nil {
stillMissing = append(stillMissing, dep)
}
}
if len(stillMissing) > 0 {
errMsg := "dependencies still missing after install attempt:\n"
for _, dep := range stillMissing {
errMsg += fmt.Sprintf(" - %s\n", dep.Name)
}
return stillMissing, fmt.Errorf("%s", errMsg)
}
fmt.Fprintf(os.Stderr, " ✓ Dependencies installed successfully\n")
return nil, nil return nil, nil
} }

View File

@ -72,9 +72,9 @@ func (bi *BinaryInstaller) ResolveBinaryPath(binary string, extraPaths ...string
return installers.ResolveBinaryPath(binary, extraPaths...) return installers.ResolveBinaryPath(binary, extraPaths...)
} }
// InstallDeBrosBinaries clones and builds DeBros binaries // InstallDeBrosBinaries builds DeBros binaries from source
func (bi *BinaryInstaller) InstallDeBrosBinaries(branch string, oramaHome string, skipRepoUpdate bool) error { func (bi *BinaryInstaller) InstallDeBrosBinaries(oramaHome string) error {
return bi.gateway.InstallDeBrosBinaries(branch, oramaHome, skipRepoUpdate) return bi.gateway.InstallDeBrosBinaries(oramaHome)
} }
// InstallSystemDependencies installs system-level dependencies via apt // InstallSystemDependencies installs system-level dependencies via apt

View File

@ -39,78 +39,9 @@ func (gi *GatewayInstaller) Configure() error {
return nil return nil
} }
// downloadSourceZIP downloads source code as ZIP from GitHub // InstallDeBrosBinaries builds DeBros binaries from source at /home/debros/src.
// This is simpler and more reliable than git clone with shallow clones // Source must already be present (uploaded via SCP archive).
func (gi *GatewayInstaller) downloadSourceZIP(branch string, srcDir string) error { func (gi *GatewayInstaller) InstallDeBrosBinaries(oramaHome string) error {
// GitHub archive URL format
zipURL := fmt.Sprintf("https://github.com/DeBrosOfficial/network/archive/refs/heads/%s.zip", branch)
zipPath := "/tmp/network-source.zip"
extractDir := "/tmp/network-extract"
// Clean up any previous download artifacts
os.RemoveAll(zipPath)
os.RemoveAll(extractDir)
// Download ZIP
fmt.Fprintf(gi.logWriter, " Downloading source (branch: %s)...\n", branch)
if err := DownloadFile(zipURL, zipPath); err != nil {
return fmt.Errorf("failed to download source from %s: %w", zipURL, err)
}
// Create extraction directory
if err := os.MkdirAll(extractDir, 0755); err != nil {
return fmt.Errorf("failed to create extraction directory: %w", err)
}
// Extract ZIP
fmt.Fprintf(gi.logWriter, " Extracting source...\n")
extractCmd := exec.Command("unzip", "-q", "-o", zipPath, "-d", extractDir)
if output, err := extractCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to extract source: %w\n%s", err, string(output))
}
// GitHub extracts to network-{branch}/ directory
extractedDir := filepath.Join(extractDir, fmt.Sprintf("network-%s", branch))
// Verify extracted directory exists
if _, err := os.Stat(extractedDir); os.IsNotExist(err) {
// Try alternative naming (GitHub may sanitize branch names)
entries, _ := os.ReadDir(extractDir)
if len(entries) == 1 && entries[0].IsDir() {
extractedDir = filepath.Join(extractDir, entries[0].Name())
} else {
return fmt.Errorf("extracted directory not found at %s", extractedDir)
}
}
// Remove existing source directory
os.RemoveAll(srcDir)
// Move extracted content to source directory
if err := os.Rename(extractedDir, srcDir); err != nil {
// Cross-filesystem fallback: copy instead of rename
fmt.Fprintf(gi.logWriter, " Moving source (cross-filesystem copy)...\n")
copyCmd := exec.Command("cp", "-r", extractedDir, srcDir)
if output, err := copyCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to move source: %w\n%s", err, string(output))
}
}
// Cleanup temp files
os.RemoveAll(zipPath)
os.RemoveAll(extractDir)
// Fix ownership
if err := exec.Command("chown", "-R", "debros:debros", srcDir).Run(); err != nil {
fmt.Fprintf(gi.logWriter, " ⚠️ Warning: failed to chown source directory: %v\n", err)
}
fmt.Fprintf(gi.logWriter, " ✓ Source downloaded\n")
return nil
}
// InstallDeBrosBinaries downloads and builds DeBros binaries
func (gi *GatewayInstaller) InstallDeBrosBinaries(branch string, oramaHome string, skipRepoUpdate bool) error {
fmt.Fprintf(gi.logWriter, " Building DeBros binaries...\n") fmt.Fprintf(gi.logWriter, " Building DeBros binaries...\n")
srcDir := filepath.Join(oramaHome, "src") srcDir := filepath.Join(oramaHome, "src")
@ -124,24 +55,9 @@ func (gi *GatewayInstaller) InstallDeBrosBinaries(branch string, oramaHome strin
return fmt.Errorf("failed to create bin directory %s: %w", binDir, err) return fmt.Errorf("failed to create bin directory %s: %w", binDir, err)
} }
// Check if source directory has content // Verify source exists
hasSourceContent := false if entries, err := os.ReadDir(srcDir); err != nil || len(entries) == 0 {
if entries, err := os.ReadDir(srcDir); err == nil && len(entries) > 0 { return fmt.Errorf("source directory is empty at %s (upload source archive first)", srcDir)
hasSourceContent = true
}
// Handle repository update/download based on skipRepoUpdate flag
if skipRepoUpdate {
fmt.Fprintf(gi.logWriter, " Skipping source download (--no-pull flag)\n")
if !hasSourceContent {
return fmt.Errorf("cannot skip download: source directory is empty at %s (need to populate it first)", srcDir)
}
fmt.Fprintf(gi.logWriter, " Using existing source at %s\n", srcDir)
} else {
// Download source as ZIP from GitHub (simpler than git, no shallow clone issues)
if err := gi.downloadSourceZIP(branch, srcDir); err != nil {
return err
}
} }
// Build binaries // Build binaries

View File

@ -52,9 +52,6 @@ type ProductionSetup struct {
serviceGenerator *SystemdServiceGenerator serviceGenerator *SystemdServiceGenerator
serviceController *SystemdController serviceController *SystemdController
binaryInstaller *BinaryInstaller binaryInstaller *BinaryInstaller
branch string
skipRepoUpdate bool
skipBuild bool // Skip all Go compilation (use pre-built binaries)
NodePeerID string // Captured during Phase3 for later display NodePeerID string // Captured during Phase3 for later display
} }
@ -86,24 +83,16 @@ func SaveBranchPreference(oramaDir, branch string) error {
} }
// NewProductionSetup creates a new production setup orchestrator // NewProductionSetup creates a new production setup orchestrator
func NewProductionSetup(oramaHome string, logWriter io.Writer, forceReconfigure bool, branch string, skipRepoUpdate bool, skipResourceChecks bool, skipBuild bool) *ProductionSetup { func NewProductionSetup(oramaHome string, logWriter io.Writer, forceReconfigure bool, skipResourceChecks bool) *ProductionSetup {
oramaDir := filepath.Join(oramaHome, ".orama") oramaDir := filepath.Join(oramaHome, ".orama")
arch, _ := (&ArchitectureDetector{}).Detect() arch, _ := (&ArchitectureDetector{}).Detect()
// If branch is empty, try to read from stored preference, otherwise default to main
if branch == "" {
branch = ReadBranchPreference(oramaDir)
}
return &ProductionSetup{ return &ProductionSetup{
oramaHome: oramaHome, oramaHome: oramaHome,
oramaDir: oramaDir, oramaDir: oramaDir,
logWriter: logWriter, logWriter: logWriter,
forceReconfigure: forceReconfigure, forceReconfigure: forceReconfigure,
arch: arch, arch: arch,
branch: branch,
skipRepoUpdate: skipRepoUpdate,
skipBuild: skipBuild,
skipResourceChecks: skipResourceChecks, skipResourceChecks: skipResourceChecks,
privChecker: &PrivilegeChecker{}, privChecker: &PrivilegeChecker{},
osDetector: &OSDetector{}, osDetector: &OSDetector{},
@ -201,12 +190,12 @@ func (ps *ProductionSetup) Phase1CheckPrerequisites() error {
ps.arch = arch ps.arch = arch
ps.logf(" ✓ Detected architecture: %s", arch) ps.logf(" ✓ Detected architecture: %s", arch)
// Check basic dependencies // Check basic dependencies (auto-installs missing ones)
depChecker := NewDependencyChecker(ps.skipOptionalDeps) depChecker := NewDependencyChecker(ps.skipOptionalDeps)
if missing, err := depChecker.CheckAll(); err != nil { if missing, err := depChecker.CheckAll(); err != nil {
ps.logf(" ❌ Missing dependencies:") ps.logf(" ❌ Failed to install dependencies:")
for _, dep := range missing { for _, dep := range missing {
ps.logf(" - %s: %s", dep.Name, dep.InstallHint) ps.logf(" - %s", dep.Name)
} }
return err return err
} }
@ -307,86 +296,30 @@ func (ps *ProductionSetup) Phase2bInstallBinaries() error {
ps.logf(" ⚠️ System dependencies warning: %v", err) ps.logf(" ⚠️ System dependencies warning: %v", err)
} }
if ps.skipBuild { // Install Go toolchain (downloads from go.dev if needed)
// --pre-built mode: skip all Go compilation, verify binaries exist if err := ps.binaryInstaller.InstallGo(); err != nil {
ps.logf(" --pre-built mode: skipping Go installation and all compilation") return fmt.Errorf("failed to install Go: %w", err)
}
// Verify required DeBros binaries exist if err := ps.binaryInstaller.InstallOlric(); err != nil {
binDir := filepath.Join(ps.oramaHome, "bin") ps.logf(" ⚠️ Olric install warning: %v", err)
requiredBins := []string{"orama-node", "gateway", "orama", "identity"} }
for _, bin := range requiredBins {
binPath := filepath.Join(binDir, bin) // Install DeBros binaries (source must be at /home/debros/src via SCP)
if _, err := os.Stat(binPath); os.IsNotExist(err) { if err := ps.binaryInstaller.InstallDeBrosBinaries(ps.oramaHome); err != nil {
return fmt.Errorf("--pre-built: required binary not found at %s (run 'make build-linux' locally and copy to VPS)", binPath) return fmt.Errorf("failed to install DeBros binaries: %w", err)
} }
ps.logf(" ✓ Found %s", binPath)
// Install CoreDNS only for nameserver nodes
if ps.isNameserver {
if err := ps.binaryInstaller.InstallCoreDNS(); err != nil {
ps.logf(" ⚠️ CoreDNS install warning: %v", err)
} }
}
// Grant CAP_NET_BIND_SERVICE to orama-node // Install Caddy on ALL nodes (any node may host namespaces and need TLS)
nodeBinary := filepath.Join(binDir, "orama-node") if err := ps.binaryInstaller.InstallCaddy(); err != nil {
if err := exec.Command("setcap", "cap_net_bind_service=+ep", nodeBinary).Run(); err != nil { ps.logf(" ⚠️ Caddy install warning: %v", err)
ps.logf(" ⚠️ Warning: failed to setcap on orama-node: %v", err)
}
// Verify Olric
if _, err := exec.LookPath("olric-server"); err != nil {
// Check if it's in the bin dir
olricPath := filepath.Join(binDir, "olric-server")
if _, err := os.Stat(olricPath); os.IsNotExist(err) {
return fmt.Errorf("--pre-built: olric-server not found in PATH or %s", binDir)
}
// Copy to /usr/local/bin
if data, err := os.ReadFile(olricPath); err == nil {
os.WriteFile("/usr/local/bin/olric-server", data, 0755)
}
ps.logf(" ✓ Found %s", olricPath)
} else {
ps.logf(" ✓ olric-server already in PATH")
}
// Verify CoreDNS and Caddy if nameserver
if ps.isNameserver {
if _, err := os.Stat("/usr/local/bin/coredns"); os.IsNotExist(err) {
return fmt.Errorf("--pre-built: coredns not found at /usr/local/bin/coredns")
}
ps.logf(" ✓ Found /usr/local/bin/coredns")
if _, err := os.Stat("/usr/bin/caddy"); os.IsNotExist(err) {
return fmt.Errorf("--pre-built: caddy not found at /usr/bin/caddy")
}
ps.logf(" ✓ Found /usr/bin/caddy")
// Grant CAP_NET_BIND_SERVICE to caddy
if err := exec.Command("setcap", "cap_net_bind_service=+ep", "/usr/bin/caddy").Run(); err != nil {
ps.logf(" ⚠️ Warning: failed to setcap on caddy: %v", err)
}
}
} else {
// Normal mode: install Go and build everything
if err := ps.binaryInstaller.InstallGo(); err != nil {
return fmt.Errorf("failed to install Go: %w", err)
}
if err := ps.binaryInstaller.InstallOlric(); err != nil {
ps.logf(" ⚠️ Olric install warning: %v", err)
}
// Install DeBros binaries (must be done before CoreDNS since we need the RQLite plugin source)
if err := ps.binaryInstaller.InstallDeBrosBinaries(ps.branch, ps.oramaHome, ps.skipRepoUpdate); err != nil {
return fmt.Errorf("failed to install DeBros binaries: %w", err)
}
// Install CoreDNS only for nameserver nodes
if ps.isNameserver {
if err := ps.binaryInstaller.InstallCoreDNS(); err != nil {
ps.logf(" ⚠️ CoreDNS install warning: %v", err)
}
}
// Install Caddy on ALL nodes (any node may host namespaces and need TLS)
if err := ps.binaryInstaller.InstallCaddy(); err != nil {
ps.logf(" ⚠️ Caddy install warning: %v", err)
}
} }
// These are pre-built binary downloads (not Go compilation), always run them // These are pre-built binary downloads (not Go compilation), always run them