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

Commit 5fad595

Browse files
committed
Add flag to sync extensions back to local
Resolves #20
1 parent 3f4919f commit 5fad595

File tree

3 files changed

+89
-19
lines changed

3 files changed

+89
-19
lines changed

.sail/Dockerfile

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
FROM codercom/ubuntu-dev-go
22

3+
# Go module tooling is completely broken.
4+
ENV GO111MODULE=off
5+
36
LABEL project_root "~/go/src/go.coder.com"

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,9 @@ This operation may take a while on a slow connections, but will be fast
5050
on follow-up connections to the same server.
5151

5252
To disable this feature entirely, pass the `--skipsync` flag.
53+
54+
### Sync-back
55+
56+
By default, VS Code changes on the remote server won't be synced back
57+
when the connection closes. To synchronize back to local when the connection ends,
58+
pass the `-b` flag.

main.go

+80-19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"os"
1111
"os/exec"
12+
"os/signal"
1213
"path/filepath"
1314
"runtime"
1415
"strconv"
@@ -24,8 +25,12 @@ func init() {
2425
}
2526

2627
func main() {
27-
skipSyncFlag := flag.Bool("skipsync", false, "skip syncing local settings and extensions to remote host")
28-
sshFlags := flag.String("ssh-flags", "", "custom SSH flags")
28+
var (
29+
skipSyncFlag = flag.Bool("skipsync", false, "skip syncing local settings and extensions to remote host")
30+
sshFlags = flag.String("ssh-flags", "", "custom SSH flags")
31+
syncBack = flag.Bool("b", false, "sync extensions back on termination")
32+
)
33+
2934
flag.Usage = func() {
3035
fmt.Printf(`Usage: [-skipsync] %v HOST [DIR] [SSH ARGS...]
3136
@@ -58,6 +63,9 @@ More info: https://github.com/codercom/sshcode
5863
"-tt",
5964
host,
6065
`/bin/bash -c 'set -euxo pipefail || exit 1
66+
# Make sure any currently running code-server is gone so we can overwrite
67+
# the binary.
68+
pkill -9 `+filepath.Base(codeServerPath)+` || true
6169
wget -q https://codesrv-ci.cdr.sh/latest-linux -O `+codeServerPath+`
6270
mkdir -p ~/.local/share/code-server
6371
cd `+filepath.Dir(codeServerPath)+`
@@ -74,17 +82,17 @@ chmod +x `+codeServerPath+`
7482
flog.Fatal("failed to update code-server: %v", err)
7583
}
7684

77-
if !(*skipSyncFlag) {
85+
if !*skipSyncFlag {
7886
start := time.Now()
7987
flog.Info("syncing settings")
80-
err = syncUserSettings(host)
88+
err = syncUserSettings(host, false)
8189
if err != nil {
8290
flog.Fatal("failed to sync settings: %v", err)
8391
}
8492
flog.Info("synced settings in %s", time.Since(start))
8593

8694
flog.Info("syncing extensions")
87-
err = syncExtensions(host)
95+
err = syncExtensions(host, false)
8896
if err != nil {
8997
flog.Fatal("failed to sync extensions: %v", err)
9098
}
@@ -131,8 +139,38 @@ chmod +x `+codeServerPath+`
131139
break
132140
}
133141

142+
ctx, cancel = context.WithCancel(context.Background())
134143
openBrowser(url)
135-
sshCmd.Wait()
144+
145+
go func() {
146+
defer cancel()
147+
sshCmd.Wait()
148+
}()
149+
150+
c := make(chan os.Signal)
151+
signal.Notify(c, os.Interrupt)
152+
153+
select {
154+
case <-ctx.Done():
155+
case <-c:
156+
}
157+
158+
if !*syncBack {
159+
flog.Info("shutting down")
160+
return
161+
}
162+
163+
flog.Info("synchronizing VS Code back to local")
164+
165+
err = syncExtensions(host, true)
166+
if err != nil {
167+
flog.Fatal("failed to sync extensions back: %v", err)
168+
}
169+
170+
err = syncUserSettings(host, true)
171+
if err != nil {
172+
flog.Fatal("failed to user settigns extensions back: %v", err)
173+
}
136174
}
137175

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

200-
func syncUserSettings(host string) error {
238+
func syncUserSettings(host string, back bool) error {
201239
localConfDir, err := configDir()
202240
if err != nil {
203241
return err
204242
}
205-
const remoteSettingsDir = ".local/share/code-server/User"
243+
const remoteSettingsDir = ".local/share/code-server/User/"
244+
245+
var (
246+
src = localConfDir + "/"
247+
dest = host + ":" + remoteSettingsDir
248+
)
249+
250+
if back {
251+
dest, src = src, dest
252+
}
206253

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

211-
func syncExtensions(host string) error {
258+
func syncExtensions(host string, back bool) error {
212259
localExtensionsDir, err := extensionsDir()
213260
if err != nil {
214261
return err
215262
}
216-
const remoteExtensionsDir = ".local/share/code-server/extensions"
263+
const remoteExtensionsDir = ".local/share/code-server/extensions/"
264+
265+
var (
266+
src = localExtensionsDir + "/"
267+
dest = host + ":" + remoteExtensionsDir
268+
)
269+
if back {
270+
dest, src = src, dest
271+
}
217272

218-
return rsync(localExtensionsDir+"/", remoteExtensionsDir, host)
273+
return rsync(src, dest)
219274
}
220275

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

228-
cmd := exec.Command("rsync", append(excludeFlags, "-azv", "--copy-unsafe-links", src, remoteDest)...)
282+
cmd := exec.Command("rsync", append(excludeFlags, "-azvr",
283+
// Only update newer directories, and sync times
284+
// to keep things simple.
285+
"-u", "--times",
286+
"--copy-unsafe-links",
287+
src, dest,
288+
)...,
289+
)
229290
cmd.Stdout = os.Stdout
230291
cmd.Stderr = os.Stderr
231292
err := cmd.Run()
232293
if err != nil {
233-
return xerrors.Errorf("failed to rsync '%s' to '%s': %w", src, remoteDest, err)
294+
return xerrors.Errorf("failed to rsync '%s' to '%s': %w", src, dest, err)
234295
}
235296

236297
return nil
@@ -240,9 +301,9 @@ func configDir() (string, error) {
240301
var path string
241302
switch runtime.GOOS {
242303
case "linux":
243-
path = os.ExpandEnv("$HOME/.config/Code/User")
304+
path = os.ExpandEnv("$HOME/.config/Code/User/")
244305
case "darwin":
245-
path = os.ExpandEnv("$HOME/Library/Application Support/Code/User")
306+
path = os.ExpandEnv("$HOME/Library/Application Support/Code/User/")
246307
default:
247308
return "", xerrors.Errorf("unsupported platform: %s", runtime.GOOS)
248309
}
@@ -253,7 +314,7 @@ func extensionsDir() (string, error) {
253314
var path string
254315
switch runtime.GOOS {
255316
case "linux", "darwin":
256-
path = os.ExpandEnv("$HOME/.vscode/extensions")
317+
path = os.ExpandEnv("$HOME/.vscode/extensions/")
257318
default:
258319
return "", xerrors.Errorf("unsupported platform: %s", runtime.GOOS)
259320
}

0 commit comments

Comments
 (0)