updater_windows.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package updater
  2. import (
  3. "context"
  4. "fmt"
  5. "log/slog"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. )
  10. func init() {
  11. UpdateStageDir = filepath.Join(os.Getenv("LOCALAPPDATA"), "Ollama", "updates")
  12. }
  13. func DoUpgrade(cancel context.CancelFunc, done chan int) error {
  14. logFile := filepath.Join(os.Getenv("LOCALAPPDATA"), "Ollama", "upgrade.log")
  15. files, err := filepath.Glob(filepath.Join(UpdateStageDir, "*", "*.exe")) // TODO generalize for multiplatform
  16. if err != nil {
  17. return fmt.Errorf("failed to lookup downloads: %s", err)
  18. }
  19. if len(files) == 0 {
  20. return fmt.Errorf("no update downloads found")
  21. } else if len(files) > 1 {
  22. // Shouldn't happen
  23. slog.Warn(fmt.Sprintf("multiple downloads found, using first one %v", files))
  24. }
  25. installerExe := files[0]
  26. slog.Info("starting upgrade with " + installerExe)
  27. slog.Info("upgrade log file " + logFile)
  28. // When running in debug mode, we'll be "verbose" and let the installer pop up and prompt
  29. installArgs := []string{
  30. "/CLOSEAPPLICATIONS", // Quit the tray app if it's still running
  31. "/LOG=" + filepath.Base(logFile), // Only relative seems reliable, so set pwd
  32. "/FORCECLOSEAPPLICATIONS", // Force close the tray app - might be needed
  33. }
  34. // When we're not in debug mode, make the upgrade as quiet as possible (no GUI, no prompts)
  35. // TODO - temporarily disable since we're pinning in debug mode for the preview
  36. // if debug := os.Getenv("OLLAMA_DEBUG"); debug == "" {
  37. installArgs = append(installArgs,
  38. "/SP", // Skip the "This will install... Do you wish to continue" prompt
  39. "/SUPPRESSMSGBOXES",
  40. "/SILENT",
  41. "/VERYSILENT",
  42. )
  43. // }
  44. // Safeguard in case we have requests in flight that need to drain...
  45. slog.Info("Waiting for server to shutdown")
  46. cancel()
  47. if done != nil {
  48. <-done
  49. } else {
  50. // Shouldn't happen
  51. slog.Warn("done chan was nil, not actually waiting")
  52. }
  53. slog.Debug(fmt.Sprintf("starting installer: %s %v", installerExe, installArgs))
  54. os.Chdir(filepath.Dir(logFile)) //nolint:errcheck
  55. cmd := exec.Command(installerExe, installArgs...)
  56. if err := cmd.Start(); err != nil {
  57. return fmt.Errorf("unable to start ollama app %w", err)
  58. }
  59. if cmd.Process != nil {
  60. err = cmd.Process.Release()
  61. if err != nil {
  62. slog.Error(fmt.Sprintf("failed to release server process: %s", err))
  63. }
  64. } else {
  65. // TODO - some details about why it didn't start, or is this a pedantic error case?
  66. return fmt.Errorf("installer process did not start")
  67. }
  68. // TODO should we linger for a moment and check to make sure it's actually running by checking the pid?
  69. slog.Info("Installer started in background, exiting")
  70. os.Exit(0)
  71. // Not reached
  72. return nil
  73. }