11package cmd
22
33import (
4- "archive/tar"
5- "compress/gzip"
64 "context"
75 "fmt"
8- "io"
9- "net/http"
106 "os"
117 "os/exec"
12- "path/filepath"
13- "runtime"
14- "strings"
158 "time"
169
1710 "github.com/xvertile/sshc/internal/version"
@@ -30,8 +23,7 @@ var updateCmd = &cobra.Command{
3023
3124This command will:
3225 1. Check GitHub for the latest release
33- 2. Download the appropriate binary for your system
34- 3. Replace the current binary with the new version
26+ 2. Run the install script to update to the latest version
3527
3628Examples:
3729 sshc update # Update to latest version if available
@@ -69,197 +61,20 @@ func runUpdate(cmd *cobra.Command, args []string) {
6961 fmt .Printf ("Reinstalling current version (%s)\n " , AppVersion )
7062 }
7163
72- // Determine OS and architecture
73- goos := runtime .GOOS
74- goarch := runtime .GOARCH
64+ fmt .Println ("Running install script..." )
7565
76- if goos != "darwin" && goos != "linux" {
77- fmt .Fprintf (os .Stderr , "Unsupported operating system: %s\n " , goos )
78- os .Exit (1 )
79- }
80-
81- if goarch != "amd64" && goarch != "arm64" {
82- fmt .Fprintf (os .Stderr , "Unsupported architecture: %s\n " , goarch )
83- os .Exit (1 )
84- }
85-
86- // Get the version to download
87- downloadVersion := updateInfo .LatestVer
88- if ! updateInfo .Available && forceUpdate {
89- downloadVersion = AppVersion
90- }
91-
92- // Strip 'v' prefix for filename
93- versionNum := strings .TrimPrefix (downloadVersion , "v" )
94-
95- // Construct download URL
96- filename := fmt .Sprintf ("sshc_%s_%s_%s.tar.gz" , versionNum , goos , goarch )
97- downloadURL := fmt .Sprintf ("https://github.com/xvertile/sshc/releases/download/%s/%s" , downloadVersion , filename )
98-
99- fmt .Printf ("Downloading %s...\n " , filename )
100-
101- // Download the release
102- resp , err := http .Get (downloadURL )
103- if err != nil {
104- fmt .Fprintf (os .Stderr , "Error downloading update: %v\n " , err )
105- os .Exit (1 )
106- }
107- defer resp .Body .Close ()
108-
109- if resp .StatusCode != http .StatusOK {
110- fmt .Fprintf (os .Stderr , "Error downloading update: HTTP %d\n " , resp .StatusCode )
111- os .Exit (1 )
112- }
113-
114- // Create temp directory
115- tmpDir , err := os .MkdirTemp ("" , "sshc-update-*" )
116- if err != nil {
117- fmt .Fprintf (os .Stderr , "Error creating temp directory: %v\n " , err )
118- os .Exit (1 )
119- }
120- defer os .RemoveAll (tmpDir )
66+ // Run the install script via curl and bash
67+ installCmd := exec .Command ("bash" , "-c" , "curl -fsSL https://raw.githubusercontent.com/xvertile/sshc/main/install/install.sh | bash" )
68+ installCmd .Stdin = os .Stdin
69+ installCmd .Stdout = os .Stdout
70+ installCmd .Stderr = os .Stderr
12171
122- // Extract the tarball
123- fmt .Println ("Extracting..." )
124- if err := extractTarGz (resp .Body , tmpDir ); err != nil {
125- fmt .Fprintf (os .Stderr , "Error extracting update: %v\n " , err )
72+ if err := installCmd .Run (); err != nil {
73+ fmt .Fprintf (os .Stderr , "Error running install script: %v\n " , err )
12674 os .Exit (1 )
12775 }
12876
129- // Find the binary
130- newBinary := filepath .Join (tmpDir , "sshc" )
131- if _ , err := os .Stat (newBinary ); os .IsNotExist (err ) {
132- fmt .Fprintf (os .Stderr , "Error: binary not found in archive\n " )
133- os .Exit (1 )
134- }
135-
136- // Get current binary path
137- currentBinary , err := os .Executable ()
138- if err != nil {
139- fmt .Fprintf (os .Stderr , "Error finding current binary: %v\n " , err )
140- os .Exit (1 )
141- }
142-
143- // Resolve symlinks
144- currentBinary , err = filepath .EvalSymlinks (currentBinary )
145- if err != nil {
146- fmt .Fprintf (os .Stderr , "Error resolving binary path: %v\n " , err )
147- os .Exit (1 )
148- }
149-
150- fmt .Printf ("Installing to %s...\n " , currentBinary )
151-
152- // Check if we need sudo
153- needSudo := false
154- if err := os .Rename (newBinary , currentBinary ); err != nil {
155- if os .IsPermission (err ) {
156- needSudo = true
157- } else {
158- // Try copying instead (cross-device link)
159- if err := copyFile (newBinary , currentBinary ); err != nil {
160- if os .IsPermission (err ) {
161- needSudo = true
162- } else {
163- fmt .Fprintf (os .Stderr , "Error installing update: %v\n " , err )
164- os .Exit (1 )
165- }
166- }
167- }
168- }
169-
170- if needSudo {
171- fmt .Println ("Need elevated privileges to install..." )
172- sudoCmd := exec .Command ("sudo" , "cp" , newBinary , currentBinary )
173- sudoCmd .Stdin = os .Stdin
174- sudoCmd .Stdout = os .Stdout
175- sudoCmd .Stderr = os .Stderr
176- if err := sudoCmd .Run (); err != nil {
177- fmt .Fprintf (os .Stderr , "Error installing with sudo: %v\n " , err )
178- os .Exit (1 )
179- }
180-
181- // Set permissions
182- chmodCmd := exec .Command ("sudo" , "chmod" , "+x" , currentBinary )
183- if err := chmodCmd .Run (); err != nil {
184- fmt .Fprintf (os .Stderr , "Error setting permissions: %v\n " , err )
185- os .Exit (1 )
186- }
187- } else {
188- // Set permissions
189- if err := os .Chmod (currentBinary , 0755 ); err != nil {
190- fmt .Fprintf (os .Stderr , "Error setting permissions: %v\n " , err )
191- os .Exit (1 )
192- }
193- }
194-
195- fmt .Printf ("\n Successfully updated to %s\n " , downloadVersion )
196-
197- // Show new version
198- versionCmd := exec .Command (currentBinary , "--version" )
199- versionCmd .Stdout = os .Stdout
200- versionCmd .Stderr = os .Stderr
201- versionCmd .Run ()
202- }
203-
204- // extractTarGz extracts a tar.gz archive to the destination directory
205- func extractTarGz (r io.Reader , dest string ) error {
206- gzr , err := gzip .NewReader (r )
207- if err != nil {
208- return err
209- }
210- defer gzr .Close ()
211-
212- tr := tar .NewReader (gzr )
213-
214- for {
215- header , err := tr .Next ()
216- if err == io .EOF {
217- break
218- }
219- if err != nil {
220- return err
221- }
222-
223- target := filepath .Join (dest , header .Name )
224-
225- switch header .Typeflag {
226- case tar .TypeDir :
227- if err := os .MkdirAll (target , 0755 ); err != nil {
228- return err
229- }
230- case tar .TypeReg :
231- f , err := os .OpenFile (target , os .O_CREATE | os .O_RDWR , os .FileMode (header .Mode ))
232- if err != nil {
233- return err
234- }
235-
236- if _ , err := io .Copy (f , tr ); err != nil {
237- f .Close ()
238- return err
239- }
240- f .Close ()
241- }
242- }
243-
244- return nil
245- }
246-
247- // copyFile copies a file from src to dst
248- func copyFile (src , dst string ) error {
249- source , err := os .Open (src )
250- if err != nil {
251- return err
252- }
253- defer source .Close ()
254-
255- destination , err := os .Create (dst )
256- if err != nil {
257- return err
258- }
259- defer destination .Close ()
260-
261- _ , err = io .Copy (destination , source )
262- return err
77+ fmt .Println ("\n Update complete!" )
26378}
26479
26580func init () {
0 commit comments