1package cmd
2
3import (
4 "fmt"
5 "os"
6
7 apphandlers "github.com/bnema/archup/internal/application/handlers"
8 "github.com/bnema/archup/internal/application/services"
9 "github.com/bnema/archup/internal/config"
10 "github.com/bnema/archup/internal/infrastructure/executor"
11 "github.com/bnema/archup/internal/infrastructure/filesystem"
12 infrahttp "github.com/bnema/archup/internal/infrastructure/http"
13 infralogger "github.com/bnema/archup/internal/infrastructure/logger"
14 "github.com/bnema/archup/internal/infrastructure/persistence"
15 "github.com/bnema/archup/internal/interfaces/tui"
16 "github.com/bnema/archup/internal/logger"
17 tea "github.com/charmbracelet/bubbletea"
18 "github.com/spf13/cobra"
19)
20
21func init() {
22 rootCmd.AddCommand(newInstallCmd())
23}
24
25func newInstallCmd() *cobra.Command {
26 var dryRun bool
27 cmd := &cobra.Command{
28 Use: "install",
29 Short: "Run base system installer",
30 RunE: func(cmd *cobra.Command, args []string) error {
31 return runInstall(dryRun)
32 },
33 }
34 cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Show TUI but don't execute commands")
35 return cmd
36}
37
38func runInstall(dryRun bool) error {
39 oldLog, err := logger.New(config.DefaultLogPath, dryRun)
40 if err != nil {
41 return fmt.Errorf("failed to create logger: %w", err)
42 }
43 defer func() {
44 if err := oldLog.Close(); err != nil {
45 fmt.Fprintf(os.Stderr, "Failed to close logger: %v\n", err)
46 }
47 }()
48
49 if dryRun {
50 oldLog.Info("Running in DRY-RUN mode - commands will be logged but not executed")
51 }
52
53 cfg := config.NewConfig(version)
54 oldLog.Info("Config initialized", "version", version, "raw_url", cfg.RawURL)
55
56 oldLog.Info("Initializing DDD architecture components")
57
58 slogAdapter := infralogger.NewSlogAdapter(oldLog.Slog())
59
60 fsAdapter := &filesystem.LocalFileSystem{}
61 shellExec := executor.NewShellExecutor(slogAdapter)
62 chrootExec := executor.NewChrootExecutor(slogAdapter)
63 httpClient := infrahttp.NewHTTPClient()
64 scriptExec := executor.NewScriptExecutor(fsAdapter, shellExec, config.DefaultInstallDir)
65 repoAdapter, err := persistence.NewFileRepository(config.DefaultConfigPath)
66 if err != nil {
67 oldLog.Error("Failed to create repository adapter", "error", err)
68 return fmt.Errorf("create repository adapter: %w", err)
69 }
70
71 bootstrapHandler := apphandlers.NewBootstrapHandler(fsAdapter, httpClient, slogAdapter, cfg.RepoURL, cfg.RawURL, cfg.Branch())
72 preflightHandler := apphandlers.NewPreflightHandler(fsAdapter, shellExec, slogAdapter)
73 partitionHandler := apphandlers.NewPartitionHandler(shellExec, slogAdapter)
74 baseHandler := apphandlers.NewInstallBaseHandler(fsAdapter, shellExec, chrootExec, slogAdapter)
75 configHandler := apphandlers.NewConfigureSystemHandler(fsAdapter, chrootExec, slogAdapter)
76 bootloaderHandler := apphandlers.NewBootloaderHandler(fsAdapter, shellExec, chrootExec, slogAdapter)
77 reposHandler := apphandlers.NewReposHandler(fsAdapter, chrootExec, slogAdapter)
78 postInstallHandler := apphandlers.NewPostInstallHandler(fsAdapter, httpClient, chrootExec, scriptExec, slogAdapter, cfg.RawURL)
79
80 installService := services.NewInstallationService(
81 repoAdapter,
82 slogAdapter,
83 bootstrapHandler,
84 preflightHandler,
85 partitionHandler,
86 baseHandler,
87 configHandler,
88 bootloaderHandler,
89 reposHandler,
90 postInstallHandler,
91 )
92
93 gpuHandler := apphandlers.NewGPUHandler(shellExec, slogAdapter)
94 tuiApp := tui.NewApp(installService, installService.Tracker(), gpuHandler, slogAdapter, version)
95
96 oldLog.Info("Starting TUI application", "version", version)
97 p := tea.NewProgram(tuiApp, tea.WithAltScreen())
98 tuiApp.SetProgram(p)
99 if _, err := p.Run(); err != nil {
100 oldLog.Error("Error running installer", "error", err)
101 return fmt.Errorf("run installer: %w", err)
102 }
103
104 if err := tuiApp.Close(); err != nil {
105 oldLog.Error("Error closing TUI app", "error", err)
106 }
107
108 return nil
109}