5
5
package main
6
6
7
7
import (
8
- "bytes"
9
8
"context"
10
9
"encoding/json"
11
10
"errors"
12
11
"fmt"
13
- "io"
14
12
"io/ioutil"
15
13
"net"
16
14
"net/http"
@@ -20,7 +18,6 @@ import (
20
18
"os/signal"
21
19
"path/filepath"
22
20
"reflect"
23
- "regexp"
24
21
"strconv"
25
22
"strings"
26
23
"syscall"
50
47
const BackendPath = "/ide-desktop/backend"
51
48
const ProductInfoPath = BackendPath + "/product-info.json"
52
49
50
+ type LaunchContext struct {
51
+ alias string
52
+ projectDir string
53
+ configDir string
54
+ systemDir string
55
+ projectConfigDir string
56
+ wsInfo * supervisor.WorkspaceInfoResponse
57
+ }
58
+
53
59
// JB startup entrypoint
54
60
func main () {
55
61
log .Init (ServiceName , Version , true , false )
62
+ log .Info (ServiceName + ": " + Version )
56
63
startTime := time .Now ()
57
64
58
65
if len (os .Args ) < 3 {
@@ -65,7 +72,13 @@ func main() {
65
72
label = os .Args [3 ]
66
73
}
67
74
68
- backendVersion , err := resolveBackendVersion ()
75
+ info , err := resolveProductInfo ()
76
+ if err != nil {
77
+ log .WithError (err ).Error ("failed to resolve product info" )
78
+ return
79
+ }
80
+
81
+ backendVersion , err := version .NewVersion (info .Version )
69
82
if err != nil {
70
83
log .WithError (err ).Error ("failed to resolve backend version" )
71
84
return
@@ -77,8 +90,8 @@ func main() {
77
90
return
78
91
}
79
92
80
- repoRoot := wsInfo .GetCheckoutLocation ()
81
- gitpodConfig , err := parseGitpodConfig (repoRoot )
93
+ projectDir := wsInfo .GetCheckoutLocation ()
94
+ gitpodConfig , err := parseGitpodConfig (projectDir )
82
95
if err != nil {
83
96
log .WithError (err ).Error ("failed to parse .gitpod.yml" )
84
97
}
@@ -95,10 +108,32 @@ func main() {
95
108
log .WithError (err ).Error ("failed to configure vmoptions" )
96
109
}
97
110
111
+ qualifier := os .Getenv ("JETBRAINS_BACKEND_QUALIFIER" )
112
+ if qualifier == "stable" {
113
+ qualifier = ""
114
+ } else {
115
+ qualifier = "-" + qualifier
116
+ }
117
+ configDir := fmt .Sprintf ("/workspace/.config/JetBrains%s" , qualifier )
118
+ launchCtx := & LaunchContext {
119
+ alias : alias ,
120
+ wsInfo : wsInfo ,
121
+ projectDir : projectDir ,
122
+ configDir : configDir ,
123
+ systemDir : fmt .Sprintf ("/workspace/.cache/JetBrains%s" , qualifier ),
124
+ projectConfigDir : fmt .Sprintf ("%s/RemoteDev-%s/%s" , configDir , info .ProductCode , strings .ReplaceAll (projectDir , "/" , "_" )),
125
+ }
126
+
127
+ // sync initial options
128
+ err = syncOptions (launchCtx )
129
+ if err != nil {
130
+ log .WithError (err ).Error ("failed to sync initial options" )
131
+ }
132
+
98
133
// install project plugins
99
134
version_2022_1 , _ := version .NewVersion ("2022.1" )
100
135
if version_2022_1 .LessThanOrEqual (backendVersion ) {
101
- err = installPlugins (repoRoot , gitpodConfig , alias )
136
+ err = installPlugins (gitpodConfig , launchCtx )
102
137
installPluginsCost := time .Now ().Local ().Sub (startTime ).Milliseconds ()
103
138
if err != nil {
104
139
log .WithError (err ).WithField ("cost" , installPluginsCost ).Error ("installing repo plugins: done" )
@@ -112,7 +147,7 @@ func main() {
112
147
if err != nil {
113
148
log .WithError (err ).Error ("failed to install gitpod-remote plugin" )
114
149
}
115
- go run (wsInfo , alias )
150
+ go run (launchCtx )
116
151
117
152
debugAgentPrefix := "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:"
118
153
http .HandleFunc ("/debug" , func (w http.ResponseWriter , r * http.Request ) {
@@ -324,13 +359,13 @@ func resolveWorkspaceInfo(ctx context.Context) (*supervisor.WorkspaceInfoRespons
324
359
return nil , errors .New ("failed with attempt 10 times" )
325
360
}
326
361
327
- func run (wsInfo * supervisor. WorkspaceInfoResponse , alias string ) {
362
+ func run (launchCtx * LaunchContext ) {
328
363
var args []string
329
364
args = append (args , "run" )
330
- args = append (args , wsInfo . GetCheckoutLocation () )
331
- cmd := remoteDevServerCmd (args )
332
- cmd .Env = append (cmd .Env , "JETBRAINS_GITPOD_BACKEND_KIND=" + alias )
333
- workspaceUrl , err := url .Parse (wsInfo .WorkspaceUrl )
365
+ args = append (args , launchCtx . projectDir )
366
+ cmd := remoteDevServerCmd (args , launchCtx )
367
+ cmd .Env = append (cmd .Env , "JETBRAINS_GITPOD_BACKEND_KIND=" + launchCtx . alias )
368
+ workspaceUrl , err := url .Parse (launchCtx . wsInfo .WorkspaceUrl )
334
369
if err == nil {
335
370
cmd .Env = append (cmd .Env , "JETBRAINS_GITPOD_WORKSPACE_HOST=" + workspaceUrl .Hostname ())
336
371
}
@@ -342,7 +377,7 @@ func run(wsInfo *supervisor.WorkspaceInfoResponse, alias string) {
342
377
}
343
378
344
379
// Nicely handle SIGTERM sinal
345
- go handleSignal (wsInfo . GetCheckoutLocation () )
380
+ go handleSignal ()
346
381
347
382
if err := cmd .Wait (); err != nil {
348
383
log .WithError (err ).Error ("failed to wait" )
@@ -351,28 +386,22 @@ func run(wsInfo *supervisor.WorkspaceInfoResponse, alias string) {
351
386
os .Exit (cmd .ProcessState .ExitCode ())
352
387
}
353
388
354
- func remoteDevServerCmd (args []string ) * exec.Cmd {
389
+ func remoteDevServerCmd (args []string , productContext * LaunchContext ) * exec.Cmd {
355
390
cmd := exec .Command (BackendPath + "/bin/remote-dev-server.sh" , args ... )
356
391
cmd .Env = os .Environ ()
357
392
358
393
// Set default config and system directories under /workspace to preserve between restarts
359
- qualifier := os .Getenv ("JETBRAINS_BACKEND_QUALIFIER" )
360
- if qualifier == "stable" {
361
- qualifier = ""
362
- } else {
363
- qualifier = "-" + qualifier
364
- }
365
394
cmd .Env = append (cmd .Env ,
366
- fmt .Sprintf ("IJ_HOST_CONFIG_BASE_DIR=/workspace/.config/JetBrains %s" , qualifier ),
367
- fmt .Sprintf ("IJ_HOST_SYSTEM_BASE_DIR=/workspace/.cache/JetBrains %s" , qualifier ),
395
+ fmt .Sprintf ("IJ_HOST_CONFIG_BASE_DIR=%s" , productContext . configDir ),
396
+ fmt .Sprintf ("IJ_HOST_SYSTEM_BASE_DIR=%s" , productContext . systemDir ),
368
397
)
369
398
370
399
cmd .Stderr = os .Stderr
371
400
cmd .Stdout = os .Stdout
372
401
return cmd
373
402
}
374
403
375
- func handleSignal (projectPath string ) {
404
+ func handleSignal () {
376
405
sigChan := make (chan os.Signal , 1 )
377
406
signal .Notify (sigChan , os .Interrupt , syscall .SIGTERM )
378
407
@@ -488,10 +517,11 @@ func updateVMOptions(
488
517
}
489
518
*/
490
519
type ProductInfo struct {
491
- Version string `json:"version"`
520
+ Version string `json:"version"`
521
+ ProductCode string `json:"productCode"`
492
522
}
493
523
494
- func resolveBackendVersion () (* version. Version , error ) {
524
+ func resolveProductInfo () (* ProductInfo , error ) {
495
525
f , err := os .Open (ProductInfoPath )
496
526
if err != nil {
497
527
return nil , err
@@ -504,52 +534,80 @@ func resolveBackendVersion() (*version.Version, error) {
504
534
505
535
var info ProductInfo
506
536
err = json .Unmarshal (content , & info )
507
- if err != nil {
508
- return nil , err
509
- }
510
- return version .NewVersion (info .Version )
537
+ return & info , err
511
538
}
512
539
513
- func installPlugins ( repoRoot string , config * gitpod. GitpodConfig , alias string ) error {
514
- plugins , err := getPlugins ( config , alias )
540
+ func syncOptions ( launchCtx * LaunchContext ) error {
541
+ userHomeDir , err := os . UserHomeDir ( )
515
542
if err != nil {
516
543
return err
517
544
}
518
- if len (plugins ) <= 0 {
545
+ var srcDirs []string
546
+ for _ , srcDir := range []string {
547
+ fmt .Sprintf ("%s/.gitpod/jetbrains/options" , userHomeDir ),
548
+ fmt .Sprintf ("%s/.gitpod/jetbrains/%s/options" , userHomeDir , launchCtx .alias ),
549
+ fmt .Sprintf ("%s/.gitpod/jetbrains/options" , launchCtx .projectDir ),
550
+ fmt .Sprintf ("%s/.gitpod/jetbrains/%s/options" , launchCtx .projectDir , launchCtx .alias ),
551
+ } {
552
+ srcStat , err := os .Stat (srcDir )
553
+ if os .IsNotExist (err ) {
554
+ // nothing to sync
555
+ continue
556
+ }
557
+ if err != nil {
558
+ return err
559
+ }
560
+ if ! srcStat .IsDir () {
561
+ return fmt .Errorf ("%s is not a directory" , srcDir )
562
+ }
563
+ srcDirs = append (srcDirs , srcDir )
564
+ }
565
+ if len (srcDirs ) == 0 {
566
+ // nothing to sync
519
567
return nil
520
568
}
521
- r , w , err := os .Pipe ()
569
+
570
+ destDir := fmt .Sprintf ("%s/options" , launchCtx .projectConfigDir )
571
+ _ , err = os .Stat (destDir )
572
+ if ! os .IsNotExist (err ) {
573
+ // already synced skipping, i.e. restart of jb backend
574
+ return nil
575
+ }
576
+ err = os .MkdirAll (destDir , os .ModePerm )
522
577
if err != nil {
523
578
return err
524
579
}
525
- defer r .Close ()
526
580
527
- outC := make (chan string )
528
- go func () {
529
- var buf bytes.Buffer
530
- _ , _ = io .Copy (& buf , r )
531
- outC <- buf .String ()
532
- }()
581
+ for _ , srcDir := range srcDirs {
582
+ cp := exec .Command ("cp" , "-rf" , srcDir + "/." , destDir )
583
+ err = cp .Run ()
584
+ if err != nil {
585
+ return err
586
+ }
587
+ }
588
+ return nil
589
+ }
590
+
591
+ func installPlugins (config * gitpod.GitpodConfig , launchCtx * LaunchContext ) error {
592
+ plugins , err := getPlugins (config , launchCtx .alias )
593
+ if err != nil {
594
+ return err
595
+ }
596
+ if len (plugins ) <= 0 {
597
+ return nil
598
+ }
533
599
534
600
var args []string
535
601
args = append (args , "installPlugins" )
536
- args = append (args , repoRoot )
602
+ args = append (args , launchCtx . projectDir )
537
603
args = append (args , plugins ... )
538
- cmd := remoteDevServerCmd (args )
539
- cmd .Stdout = io .MultiWriter (w , os .Stdout )
604
+ cmd := remoteDevServerCmd (args , launchCtx )
540
605
installErr := cmd .Run ()
541
606
542
607
// delete alien_plugins.txt to suppress 3rd-party plugins consent on startup to workaround backend startup freeze
543
- w .Close ()
544
- out := <- outC
545
- configR := regexp .MustCompile ("IDE config directory: (\\ S+)\n " )
546
- matches := configR .FindStringSubmatch (out )
547
- if len (matches ) == 2 {
548
- configDir := matches [1 ]
549
- err := os .Remove (configDir + "/alien_plugins.txt" )
550
- if err != nil {
551
- log .WithError (err ).Error ("failed to suppress 3rd-party plugins consent" )
552
- }
608
+ err = os .Remove (launchCtx .projectConfigDir + "/alien_plugins.txt" )
609
+ if err != nil {
610
+ log .WithError (err ).Error ("failed to suppress 3rd-party plugins consent" )
553
611
}
554
612
555
613
if installErr != nil {
0 commit comments