Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/finch/virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,6 @@ func virtualMachineCommands(
fp,
fs,
disk.NewUserDataDiskManager(ncc, ecc, &afero.OsFs{}, fp, finchRootPath, fc, logger),
finchRootPath,
)
}
2 changes: 2 additions & 0 deletions cmd/finch/virtual_machine_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func newVirtualMachineCommand(
fp path.Finch,
fs afero.Fs,
diskManager disk.UserDataDiskManager,
finchRootPath string,
) *cobra.Command {
virtualMachineCommand := &cobra.Command{
Use: virtualMachineRootCmd,
Expand All @@ -57,6 +58,7 @@ func newVirtualMachineCommand(
fp.LimaSSHPrivateKeyPath(), diskManager),
newSettingsVMCommand(logger, lca, fs, os.Stdout),
newDiskVMCommand(limaCmdCreator, logger),
newUpdateOSVMCommand(logger, finchRootPath),
)

return virtualMachineCommand
Expand Down
6 changes: 3 additions & 3 deletions cmd/finch/virtual_machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import (
func TestVirtualMachineCommand(t *testing.T) {
t.Parallel()

cmd := newVirtualMachineCommand(nil, nil, nil, nil, nil, "", nil, nil)
cmd := newVirtualMachineCommand(nil, nil, nil, nil, nil, "", nil, nil, "")
assert.Equal(t, cmd.Use, virtualMachineRootCmd)

// check the number of subcommand for vm
expectedCmds := 6
expectedCmds := 7
if runtime.GOOS == "darwin" {
expectedCmds = 7 // Darwin includes disk commands
expectedCmds = 8 // Darwin includes disk commands
}
assert.Equal(t, len(cmd.Commands()), expectedCmds)
}
Expand Down
115 changes: 115 additions & 0 deletions cmd/finch/virtual_machine_update_os.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//go:build darwin || windows

package main

import (
"fmt"
"runtime"

"github.com/spf13/cobra"

"github.com/runfinch/finch/pkg/flog"
"github.com/runfinch/finch/pkg/update"
)

func newUpdateOSVMCommand(logger flog.Logger, finchRootPath string) *cobra.Command {
updateOSCommand := &cobra.Command{
Use: "update-os",
Short: "Check for virtual machine OS updates",
Long: "Check for available OS updates to the Finch virtual machine. Use --install to download and install updates.",
RunE: newUpdateOSVMAction(logger, finchRootPath).runAdapter,
}
updateOSCommand.Flags().BoolP("install", "i", false, "Download and install the update if available")
return updateOSCommand
}

type updateOSVMAction struct {
logger flog.Logger
finchRootPath string
}

func newUpdateOSVMAction(logger flog.Logger, finchRootPath string) *updateOSVMAction {
return &updateOSVMAction{
logger: logger,
finchRootPath: finchRootPath,
}
}

func (uva *updateOSVMAction) runAdapter(cmd *cobra.Command, _ []string) error {
install, _ := cmd.Flags().GetBool("install")
return uva.run(!install)
}

func (uva *updateOSVMAction) run(checkOnly bool) error {
var finchPath string
switch runtime.GOOS {
case "darwin":
finchPath = "/Applications/Finch"
case "windows":
finchPath = "C:\\Program Files\\Finch"
}

updater := update.NewOSUpdater(finchPath)

if checkOnly {
return uva.checkForUpdates(updater)
}

return uva.applyUpdate(updater)
}

type osUpdater interface {
CheckForUpdates() (*update.Status, error)
ApplyUpdate() error
}

func (uva *updateOSVMAction) checkForUpdates(updater osUpdater) error {
uva.logger.Info("Checking for OS updates...")

status, err := updater.CheckForUpdates()
if err != nil {
return fmt.Errorf("failed to check for updates: %w", err)
}

if status.UpdateAvailable {
uva.logger.Infof("OS update available: %s to %s", status.CurrentVersion, status.LatestVersion)
uva.logger.Info("Run 'finch vm update-os --install' to apply the update")
} else {
if status.CurrentVersion != "" {
uva.logger.Infof("OS is up to date: %s", status.CurrentVersion)
} else {
uva.logger.Info("No OS version found. Run 'finch vm init' first.")
}
}

return nil
}

func (uva *updateOSVMAction) applyUpdate(updater osUpdater) error {
uva.logger.Info("Checking for OS updates...")

status, err := updater.CheckForUpdates()
if err != nil {
return fmt.Errorf("failed to check for updates: %w", err)
}

if !status.UpdateAvailable {
if status.CurrentVersion != "" {
uva.logger.Infof("OS is already up to date: %s", status.CurrentVersion)
} else {
uva.logger.Info("No OS version found. Run 'finch vm init' first.")
}
return nil
}

uva.logger.Infof("Updating OS from %s to %s...", status.CurrentVersion, status.LatestVersion)
uva.logger.Warnln("This will stop and recreate the VM. All containers and data will be lost.")

err = updater.ApplyUpdate()
if err != nil {
return fmt.Errorf("failed to apply OS update: %w", err)
}

uva.logger.Info("OS update completed successfully")
return nil
}
Loading
Loading