diff --git a/CHANGELOG.md b/CHANGELOG.md index c8ff78d..52b68c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,21 @@ The format is based on [Keep a Changelog][keepachangelog] and adheres to [Semant ### Deprecated ### Fixed +## [0.67.7] - 2025-11-11 + +### Added +- Added support for specifying the Git branch (main or nightly) during `prod install` and `prod upgrade`. +- The chosen branch is now saved and automatically used for future upgrades unless explicitly overridden. + +### Changed +- Updated help messages and examples for production commands to include branch options. + +### Deprecated + +### Removed + +### Fixed +\n ## [0.67.6] - 2025-11-11 ### Added diff --git a/Makefile b/Makefile index 41f653b..d73584f 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ test-e2e: .PHONY: build clean test run-node run-node2 run-node3 run-example deps tidy fmt vet lint clear-ports install-hooks kill -VERSION := 0.67.6 +VERSION := 0.67.7 COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)' diff --git a/pkg/cli/prod_commands.go b/pkg/cli/prod_commands.go index 8c5c125..ab27e91 100644 --- a/pkg/cli/prod_commands.go +++ b/pkg/cli/prod_commands.go @@ -53,21 +53,29 @@ func showProdHelp() { fmt.Printf(" --peers ADDRS - Comma-separated bootstrap peers (for non-bootstrap)\n") fmt.Printf(" --bootstrap-join ADDR - Bootstrap raft join address (for secondary bootstrap)\n") fmt.Printf(" --domain DOMAIN - Domain for HTTPS (optional)\n") + fmt.Printf(" --branch BRANCH - Git branch to use (main or nightly, default: main)\n") fmt.Printf(" upgrade - Upgrade existing installation (requires root/sudo)\n") fmt.Printf(" Options:\n") fmt.Printf(" --restart - Automatically restart services after upgrade\n") + fmt.Printf(" --branch BRANCH - Git branch to use (main or nightly, uses saved preference if not specified)\n") fmt.Printf(" status - Show status of production services\n") fmt.Printf(" logs - View production service logs\n") fmt.Printf(" Options:\n") fmt.Printf(" --follow - Follow logs in real-time\n") fmt.Printf(" uninstall - Remove production services (requires root/sudo)\n\n") fmt.Printf("Examples:\n") - fmt.Printf(" # Bootstrap node\n") + fmt.Printf(" # Bootstrap node (main branch)\n") fmt.Printf(" sudo dbn prod install --bootstrap\n\n") + fmt.Printf(" # Bootstrap node (nightly branch)\n") + fmt.Printf(" sudo dbn prod install --bootstrap --branch nightly\n\n") fmt.Printf(" # Join existing cluster\n") fmt.Printf(" sudo dbn prod install --vps-ip 10.0.0.2 --peers /ip4/10.0.0.1/tcp/4001/p2p/Qm...\n\n") fmt.Printf(" # Secondary bootstrap joining existing cluster\n") fmt.Printf(" sudo dbn prod install --bootstrap --vps-ip 10.0.0.2 --bootstrap-join 10.0.0.1:7001\n\n") + fmt.Printf(" # Upgrade using saved branch preference\n") + fmt.Printf(" sudo dbn prod upgrade --restart\n\n") + fmt.Printf(" # Upgrade and switch to nightly branch\n") + fmt.Printf(" sudo dbn prod upgrade --restart --branch nightly\n\n") fmt.Printf(" dbn prod status\n") fmt.Printf(" dbn prod logs node --follow\n") } @@ -76,7 +84,7 @@ func handleProdInstall(args []string) { // Parse arguments force := false isBootstrap := false - var vpsIP, domain, peersStr, bootstrapJoin string + var vpsIP, domain, peersStr, bootstrapJoin, branch string for i, arg := range args { switch arg { @@ -100,9 +108,24 @@ func handleProdInstall(args []string) { if i+1 < len(args) { bootstrapJoin = args[i+1] } + case "--branch": + if i+1 < len(args) { + branch = args[i+1] + } } } + // Validate branch if provided + if branch != "" && branch != "main" && branch != "nightly" { + fmt.Fprintf(os.Stderr, "āŒ Invalid branch: %s (must be 'main' or 'nightly')\n", branch) + os.Exit(1) + } + + // Default to main if not specified + if branch == "" { + branch = "main" + } + // Parse bootstrap peers if provided var bootstrapPeers []string if peersStr != "" { @@ -123,7 +146,13 @@ func handleProdInstall(args []string) { } debrosHome := "/home/debros" - setup := production.NewProductionSetup(debrosHome, os.Stdout, force) + debrosDir := debrosHome + "/.debros" + setup := production.NewProductionSetup(debrosHome, os.Stdout, force, branch) + + // Save branch preference for future upgrades + if err := production.SaveBranchPreference(debrosDir, branch); err != nil { + fmt.Fprintf(os.Stderr, "āš ļø Warning: Failed to save branch preference: %v\n", err) + } // Phase 1: Check prerequisites fmt.Printf("\nšŸ“‹ Phase 1: Checking prerequisites...\n") @@ -191,13 +220,25 @@ func handleProdUpgrade(args []string) { // Parse arguments force := false restartServices := false - for _, arg := range args { + branch := "" + for i, arg := range args { if arg == "--force" { force = true } if arg == "--restart" { restartServices = true } + if arg == "--branch" { + if i+1 < len(args) { + branch = args[i+1] + } + } + } + + // Validate branch if provided + if branch != "" && branch != "main" && branch != "nightly" { + fmt.Fprintf(os.Stderr, "āŒ Invalid branch: %s (must be 'main' or 'nightly')\n", branch) + os.Exit(1) } if os.Geteuid() != 0 { @@ -206,11 +247,25 @@ func handleProdUpgrade(args []string) { } debrosHome := "/home/debros" + debrosDir := debrosHome + "/.debros" fmt.Printf("šŸ”„ Upgrading production installation...\n") fmt.Printf(" This will preserve existing configurations and data\n") fmt.Printf(" Configurations will be updated to latest format\n\n") - setup := production.NewProductionSetup(debrosHome, os.Stdout, force) + setup := production.NewProductionSetup(debrosHome, os.Stdout, force, branch) + + // If branch was explicitly provided, save it for future upgrades + if branch != "" { + if err := production.SaveBranchPreference(debrosDir, branch); err != nil { + fmt.Fprintf(os.Stderr, "āš ļø Warning: Failed to save branch preference: %v\n", err) + } else { + fmt.Printf(" Using branch: %s (saved for future upgrades)\n", branch) + } + } else { + // Show which branch is being used (read from saved preference) + currentBranch := production.ReadBranchPreference(debrosDir) + fmt.Printf(" Using branch: %s (from saved preference)\n", currentBranch) + } // Phase 1: Check prerequisites fmt.Printf("\nšŸ“‹ Phase 1: Checking prerequisites...\n") diff --git a/pkg/environments/production/orchestrator.go b/pkg/environments/production/orchestrator.go index 91108e9..ad30886 100644 --- a/pkg/environments/production/orchestrator.go +++ b/pkg/environments/production/orchestrator.go @@ -34,18 +34,50 @@ type ProductionSetup struct { NodePeerID string // Captured during Phase3 for later display } +// ReadBranchPreference reads the stored branch preference from disk +func ReadBranchPreference(debrosDir string) string { + branchFile := filepath.Join(debrosDir, ".branch") + data, err := os.ReadFile(branchFile) + if err != nil { + return "main" // Default to main if file doesn't exist + } + branch := strings.TrimSpace(string(data)) + if branch == "" { + return "main" + } + return branch +} + +// SaveBranchPreference saves the branch preference to disk +func SaveBranchPreference(debrosDir, branch string) error { + branchFile := filepath.Join(debrosDir, ".branch") + if err := os.MkdirAll(debrosDir, 0755); err != nil { + return fmt.Errorf("failed to create debros directory: %w", err) + } + if err := os.WriteFile(branchFile, []byte(branch), 0644); err != nil { + return fmt.Errorf("failed to save branch preference: %w", err) + } + exec.Command("chown", "debros:debros", branchFile).Run() + return nil +} + // NewProductionSetup creates a new production setup orchestrator -func NewProductionSetup(debrosHome string, logWriter io.Writer, forceReconfigure bool) *ProductionSetup { +func NewProductionSetup(debrosHome string, logWriter io.Writer, forceReconfigure bool, branch string) *ProductionSetup { debrosDir := debrosHome + "/.debros" arch, _ := (&ArchitectureDetector{}).Detect() + // If branch is empty, try to read from stored preference, otherwise default to main + if branch == "" { + branch = ReadBranchPreference(debrosDir) + } + return &ProductionSetup{ debrosHome: debrosHome, debrosDir: debrosDir, logWriter: logWriter, forceReconfigure: forceReconfigure, arch: arch, - branch: "main", + branch: branch, privChecker: &PrivilegeChecker{}, osDetector: &OSDetector{}, archDetector: &ArchitectureDetector{}, diff --git a/scripts/install-debros-network.sh b/scripts/install-debros-network.sh index ae88a07..5a32818 100755 --- a/scripts/install-debros-network.sh +++ b/scripts/install-debros-network.sh @@ -193,9 +193,12 @@ echo -e "${GREEN}Installation complete!${NOCOLOR}" echo "" echo -e "${CYAN}Next, run the production setup:${NOCOLOR}" echo "" -echo "Bootstrap node (first node):" +echo "Bootstrap node (first node, main branch):" echo -e " ${BLUE}sudo dbn prod install --bootstrap${NOCOLOR}" echo "" +echo "Bootstrap node (nightly branch):" +echo -e " ${BLUE}sudo dbn prod install --bootstrap --branch nightly${NOCOLOR}" +echo "" echo "Secondary node (join existing cluster):" echo -e " ${BLUE}sudo dbn prod install --vps-ip --peers ${NOCOLOR}" echo ""