Skip to content
This repository was archived by the owner on Jan 17, 2021. It is now read-only.

Add flag to sync extensions back to local #22

Merged
merged 1 commit into from
Apr 20, 2019
Merged
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
3 changes: 3 additions & 0 deletions .sail/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
FROM codercom/ubuntu-dev-go

# Go module tooling is completely broken.
ENV GO111MODULE=off

LABEL project_root "~/go/src/go.coder.com"
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ This operation may take a while on a slow connections, but will be fast
on follow-up connections to the same server.

To disable this feature entirely, pass the `--skipsync` flag.

### Sync-back

By default, VS Code changes on the remote server won't be synced back
when the connection closes. To synchronize back to local when the connection ends,
pass the `-b` flag.
99 changes: 80 additions & 19 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strconv"
Expand All @@ -24,8 +25,12 @@ func init() {
}

func main() {
skipSyncFlag := flag.Bool("skipsync", false, "skip syncing local settings and extensions to remote host")
sshFlags := flag.String("ssh-flags", "", "custom SSH flags")
var (
skipSyncFlag = flag.Bool("skipsync", false, "skip syncing local settings and extensions to remote host")
sshFlags = flag.String("ssh-flags", "", "custom SSH flags")
syncBack = flag.Bool("b", false, "sync extensions back on termination")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make this flag more descriptive, like -syncback and ssh-flags should also become sshflags for consistency.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I choose the shorter flag because I felt like this would be a common option. I can add short versions of both though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats fair, lets leave it at -b, I think less ways to do the same thing is better.

)

flag.Usage = func() {
fmt.Printf(`Usage: [-skipsync] %v HOST [DIR] [SSH ARGS...]

Expand Down Expand Up @@ -58,6 +63,9 @@ More info: https://github.com/codercom/sshcode
"-tt",
host,
`/bin/bash -c 'set -euxo pipefail || exit 1
# Make sure any currently running code-server is gone so we can overwrite
# the binary.
pkill -9 `+filepath.Base(codeServerPath)+` || true
wget -q https://codesrv-ci.cdr.sh/latest-linux -O `+codeServerPath+`
mkdir -p ~/.local/share/code-server
cd `+filepath.Dir(codeServerPath)+`
Expand All @@ -74,17 +82,17 @@ chmod +x `+codeServerPath+`
flog.Fatal("failed to update code-server: %v", err)
}

if !(*skipSyncFlag) {
if !*skipSyncFlag {
start := time.Now()
flog.Info("syncing settings")
err = syncUserSettings(host)
err = syncUserSettings(host, false)
if err != nil {
flog.Fatal("failed to sync settings: %v", err)
}
flog.Info("synced settings in %s", time.Since(start))

flog.Info("syncing extensions")
err = syncExtensions(host)
err = syncExtensions(host, false)
if err != nil {
flog.Fatal("failed to sync extensions: %v", err)
}
Expand Down Expand Up @@ -131,8 +139,38 @@ chmod +x `+codeServerPath+`
break
}

ctx, cancel = context.WithCancel(context.Background())
openBrowser(url)
sshCmd.Wait()

go func() {
defer cancel()
sshCmd.Wait()
}()

c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)

select {
case <-ctx.Done():
case <-c:
}

if !*syncBack {
flog.Info("shutting down")
return
}

flog.Info("synchronizing VS Code back to local")

err = syncExtensions(host, true)
if err != nil {
flog.Fatal("failed to sync extensions back: %v", err)
}

err = syncUserSettings(host, true)
if err != nil {
flog.Fatal("failed to user settigns extensions back: %v", err)
}
}

func openBrowser(url string) {
Expand Down Expand Up @@ -197,40 +235,63 @@ func randomPort() (string, error) {
return "", xerrors.Errorf("max number of tries exceeded: %d", maxTries)
}

func syncUserSettings(host string) error {
func syncUserSettings(host string, back bool) error {
localConfDir, err := configDir()
if err != nil {
return err
}
const remoteSettingsDir = ".local/share/code-server/User"
const remoteSettingsDir = ".local/share/code-server/User/"

var (
src = localConfDir + "/"
dest = host + ":" + remoteSettingsDir
)

if back {
dest, src = src, dest
}

// Append "/" to have rsync copy the contents of the dir.
return rsync(localConfDir+"/", remoteSettingsDir, host, "workspaceStorage", "logs", "CachedData")
return rsync(src, dest, "workspaceStorage", "logs", "CachedData")
}

func syncExtensions(host string) error {
func syncExtensions(host string, back bool) error {
localExtensionsDir, err := extensionsDir()
if err != nil {
return err
}
const remoteExtensionsDir = ".local/share/code-server/extensions"
const remoteExtensionsDir = ".local/share/code-server/extensions/"

var (
src = localExtensionsDir + "/"
dest = host + ":" + remoteExtensionsDir
)
if back {
dest, src = src, dest
}

return rsync(localExtensionsDir+"/", remoteExtensionsDir, host)
return rsync(src, dest)
}

func rsync(src string, dest string, host string, excludePaths ...string) error {
remoteDest := fmt.Sprintf("%s:%s", host, dest)
func rsync(src string, dest string, excludePaths ...string) error {
excludeFlags := make([]string, len(excludePaths))
for i, path := range excludePaths {
excludeFlags[i] = "--exclude=" + path
}

cmd := exec.Command("rsync", append(excludeFlags, "-azv", "--copy-unsafe-links", src, remoteDest)...)
cmd := exec.Command("rsync", append(excludeFlags, "-azvr",
// Only update newer directories, and sync times
// to keep things simple.
"-u", "--times",
"--copy-unsafe-links",
src, dest,
)...,
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return xerrors.Errorf("failed to rsync '%s' to '%s': %w", src, remoteDest, err)
return xerrors.Errorf("failed to rsync '%s' to '%s': %w", src, dest, err)
}

return nil
Expand All @@ -240,9 +301,9 @@ func configDir() (string, error) {
var path string
switch runtime.GOOS {
case "linux":
path = os.ExpandEnv("$HOME/.config/Code/User")
path = os.ExpandEnv("$HOME/.config/Code/User/")
case "darwin":
path = os.ExpandEnv("$HOME/Library/Application Support/Code/User")
path = os.ExpandEnv("$HOME/Library/Application Support/Code/User/")
default:
return "", xerrors.Errorf("unsupported platform: %s", runtime.GOOS)
}
Expand All @@ -253,7 +314,7 @@ func extensionsDir() (string, error) {
var path string
switch runtime.GOOS {
case "linux", "darwin":
path = os.ExpandEnv("$HOME/.vscode/extensions")
path = os.ExpandEnv("$HOME/.vscode/extensions/")
default:
return "", xerrors.Errorf("unsupported platform: %s", runtime.GOOS)
}
Expand Down