github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/agent/install.go (about) 1 package agent 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 8 "github.com/google/uuid" 9 10 "github.com/mutagen-io/mutagen/pkg/filesystem" 11 "github.com/mutagen-io/mutagen/pkg/logging" 12 "github.com/mutagen-io/mutagen/pkg/prompting" 13 ) 14 15 // Install installs the current binary to the appropriate location for an agent 16 // binary with the current Mutagen version. 17 func Install() error { 18 // Compute the destination. 19 destination, err := installPath() 20 if err != nil { 21 return fmt.Errorf("unable to compute agent destination: %w", err) 22 } 23 24 // Compute the path to the current executable. 25 executablePath, err := os.Executable() 26 if err != nil { 27 return fmt.Errorf("unable to determine executable path: %w", err) 28 } 29 30 // Relocate the current executable to the installation path. 31 if err = filesystem.Rename(nil, executablePath, nil, destination, true); err != nil { 32 return fmt.Errorf("unable to relocate agent executable: %w", err) 33 } 34 35 // Success. 36 return nil 37 } 38 39 // install attempts to probe an endpoint and install the appropriate agent 40 // binary over the specified transport. 41 func install(logger *logging.Logger, transport Transport, prompter string) error { 42 // Detect the target platform. 43 goos, goarch, posix, err := probe(transport, prompter) 44 if err != nil { 45 return fmt.Errorf("unable to probe remote platform: %w", err) 46 } 47 48 // Find the appropriate agent binary. Ensure that it's cleaned up when we're 49 // done with it. 50 if err := prompting.Message(prompter, "Extracting agent..."); err != nil { 51 return fmt.Errorf("unable to message prompter: %w", err) 52 } 53 agentExecutable, err := ExecutableForPlatform(goos, goarch, "") 54 if err != nil { 55 return fmt.Errorf("unable to get agent for platform: %w", err) 56 } 57 defer os.Remove(agentExecutable) 58 59 // Copy the agent to the remote. We use a unique identifier for the 60 // temporary destination. For Windows remotes, we add a ".exe" suffix, which 61 // will automatically make the file executable on the remote (POSIX systems 62 // are handled separately below). For POSIX systems, we add a dot prefix to 63 // hide the executable. 64 if err := prompting.Message(prompter, "Copying agent..."); err != nil { 65 return fmt.Errorf("unable to message prompter: %w", err) 66 } 67 randomUUID, err := uuid.NewRandom() 68 if err != nil { 69 return fmt.Errorf("unable to generate UUID for agent copying: %w", err) 70 } 71 destination := BaseName + randomUUID.String() 72 if goos == "windows" { 73 destination += ".exe" 74 } 75 if posix { 76 destination = "." + destination 77 } 78 if err = transport.Copy(agentExecutable, destination); err != nil { 79 return fmt.Errorf("unable to copy agent binary: %w", err) 80 } 81 82 // For cases where we're copying from a Windows system to a POSIX remote, 83 // invoke "chmod +x" to add executability back to the copied binary. This is 84 // necessary under the specified circumstances because as soon as the agent 85 // binary is extracted from the bundle, it will lose its executability bit 86 // since Windows can't preserve this. This will also be applied to Windows 87 // POSIX remotes, but a "chmod +x" there will just be a no-op. 88 if runtime.GOOS == "windows" && posix { 89 if err := prompting.Message(prompter, "Setting agent executability..."); err != nil { 90 return fmt.Errorf("unable to message prompter: %w", err) 91 } 92 executabilityCommand := fmt.Sprintf("chmod +x %s", destination) 93 if err := run(transport, executabilityCommand); err != nil { 94 return fmt.Errorf("unable to set agent executability: %w", err) 95 } 96 } 97 98 // Invoke the remote installation. 99 if err := prompting.Message(prompter, "Installing agent..."); err != nil { 100 return fmt.Errorf("unable to message prompter: %w", err) 101 } 102 var installCommand string 103 if posix { 104 installCommand = fmt.Sprintf("./%s %s", destination, CommandInstall) 105 } else { 106 installCommand = fmt.Sprintf("%s %s", destination, CommandInstall) 107 } 108 if err := run(transport, installCommand); err != nil { 109 return fmt.Errorf("unable to invoke agent installation: %w", err) 110 } 111 112 // Success. 113 return nil 114 }