From f6f894f3a5f172792db67bfe4ef6d12378a75b39 Mon Sep 17 00:00:00 2001 From: Yaohui Wang Date: Mon, 20 Jun 2022 20:17:35 +0800 Subject: [PATCH] [jb] enable vmoptions config in .gitpod.yml --- .../gitpod-protocol/data/gitpod-schema.json | 16 +++++++ .../gitpod-protocol/go/gitpod-config-types.go | 3 ++ components/gitpod-protocol/src/protocol.ts | 1 + .../ide/jetbrains/image/status/BUILD.yaml | 1 + components/ide/jetbrains/image/status/go.mod | 4 ++ components/ide/jetbrains/image/status/go.sum | 1 + components/ide/jetbrains/image/status/main.go | 42 ++++++++++++++----- .../ide/jetbrains/image/status/main_test.go | 13 +++++- .../image/status/testdata/.gitpod.yml | 31 ++++++++++++++ 9 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 components/ide/jetbrains/image/status/testdata/.gitpod.yml diff --git a/components/gitpod-protocol/data/gitpod-schema.json b/components/gitpod-protocol/data/gitpod-schema.json index 3f21b066553a05..1fe2cb2e648ea6 100644 --- a/components/gitpod-protocol/data/gitpod-schema.json +++ b/components/gitpod-protocol/data/gitpod-schema.json @@ -287,6 +287,10 @@ "description": "Whether only stable, latest or both versions should be warmed up. Default is stable only." } } + }, + "vmoptions": { + "type": "string", + "description": "Configure JVM options, for instance '-Xmx=4096m'." } } }, @@ -317,6 +321,10 @@ "description": "Whether only stable, latest or both versions should be warmed up. Default is stable only." } } + }, + "vmoptions": { + "type": "string", + "description": "Configure JVM options, for instance '-Xmx=4096m'." } } }, @@ -347,6 +355,10 @@ "description": "Whether only stable, latest or both versions should be warmed up. Default is stable only." } } + }, + "vmoptions": { + "type": "string", + "description": "Configure JVM options, for instance '-Xmx=4096m'." } } }, @@ -377,6 +389,10 @@ "description": "Whether only stable, latest or both versions should be warmed up. Default is stable only." } } + }, + "vmoptions": { + "type": "string", + "description": "Configure JVM options, for instance '-Xmx=4096m'." } } } diff --git a/components/gitpod-protocol/go/gitpod-config-types.go b/components/gitpod-protocol/go/gitpod-config-types.go index b6f29108a4d623..e55b8371dfba2e 100644 --- a/components/gitpod-protocol/go/gitpod-config-types.go +++ b/components/gitpod-protocol/go/gitpod-config-types.go @@ -175,6 +175,9 @@ type JetBrainsProduct struct { // Enable warming up of JetBrains product in prebuilds Prebuilds *JetBrainsPrebuilds `yaml:"prebuilds,omitempty"` + + // JVM Options for IDE backend server, separated by space + VMOptions string `yaml:"vmoptions,omitempty"` } // Enable warming up of JetBrains product in prebuilds diff --git a/components/gitpod-protocol/src/protocol.ts b/components/gitpod-protocol/src/protocol.ts index d7378f3594131d..01c6dd0d6a3da5 100644 --- a/components/gitpod-protocol/src/protocol.ts +++ b/components/gitpod-protocol/src/protocol.ts @@ -640,6 +640,7 @@ export interface JetBrainsConfig { } export interface JetBrainsProductConfig { prebuilds?: JetBrainsPrebuilds; + vmoptions?: string; } export interface JetBrainsPrebuilds { version?: "stable" | "latest" | "both"; diff --git a/components/ide/jetbrains/image/status/BUILD.yaml b/components/ide/jetbrains/image/status/BUILD.yaml index eb28e751aed174..7eb5275fad8b51 100644 --- a/components/ide/jetbrains/image/status/BUILD.yaml +++ b/components/ide/jetbrains/image/status/BUILD.yaml @@ -2,6 +2,7 @@ packages: - name: app type: go srcs: + - "testdata/**" - "**/*.go" - "go.mod" - "go.sum" diff --git a/components/ide/jetbrains/image/status/go.mod b/components/ide/jetbrains/image/status/go.mod index 64f32a7bf8c85b..bc2d0408d3d36d 100644 --- a/components/ide/jetbrains/image/status/go.mod +++ b/components/ide/jetbrains/image/status/go.mod @@ -5,13 +5,17 @@ go 1.18 require ( github.com/gitpod-io/gitpod/supervisor/api v0.0.0-00010101000000-000000000000 github.com/google/go-cmp v0.5.7 + github.com/stretchr/testify v1.7.0 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/mock v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) require ( diff --git a/components/ide/jetbrains/image/status/go.sum b/components/ide/jetbrains/image/status/go.sum index 9d66e070c6b8c8..2613f932428d57 100644 --- a/components/ide/jetbrains/image/status/go.sum +++ b/components/ide/jetbrains/image/status/go.sum @@ -428,6 +428,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/components/ide/jetbrains/image/status/main.go b/components/ide/jetbrains/image/status/main.go index 3e7ec3d79dc3e8..8c9932133a53fe 100644 --- a/components/ide/jetbrains/image/status/main.go +++ b/components/ide/jetbrains/image/status/main.go @@ -76,9 +76,14 @@ func main() { } log.WithField("cost", time.Now().Local().Sub(startTime).Milliseconds()).Info("content available") + repoRoot := wsInfo.GetCheckoutLocation() + gitpodConfig, err := parseGitpodConfig(repoRoot) + if err != nil { + log.WithError(err).Error("failed to parse .gitpod.yml") + } version_2022_1, _ := version.NewVersion("2022.1") if version_2022_1.LessThanOrEqual(backendVersion) { - err = installPlugins(wsInfo, alias) + err = installPlugins(repoRoot, gitpodConfig, alias) installPluginsCost := time.Now().Local().Sub(startTime).Milliseconds() if err != nil { log.WithError(err).WithField("cost", installPluginsCost).Error("installing repo plugins: done") @@ -87,7 +92,7 @@ func main() { } } - err = configureVMOptions(alias) + err = configureVMOptions(gitpodConfig, alias) if err != nil { log.WithError(err).Error("failed to configure vmoptions") } @@ -307,7 +312,7 @@ func handleSignal(projectPath string) { log.Info("asked IDE to terminate") } -func configureVMOptions(alias string) error { +func configureVMOptions(config *gitpod.GitpodConfig, alias string) error { idePrefix := alias if alias == "intellij" { idePrefix = "idea" @@ -318,7 +323,7 @@ func configureVMOptions(alias string) error { if err != nil { return err } - newContent := updateVMOptions(alias, string(content)) + newContent := updateVMOptions(config, alias, string(content)) return ioutil.WriteFile(path, []byte(newContent), 0) } @@ -340,7 +345,7 @@ func deduplicateVMOption(oldLines []string, newLines []string, predicate func(l, return result } -func updateVMOptions(alias string, content string) string { +func updateVMOptions(config *gitpod.GitpodConfig, alias string, content string) string { // inspired by how intellij platform merge the VMOptions // https://github.com/JetBrains/intellij-community/blob/master/platform/platform-impl/src/com/intellij/openapi/application/ConfigImportHelper.java#L1115 filterFunc := func(l, r string) bool { @@ -358,12 +363,24 @@ func updateVMOptions(alias string, content string) string { gitpodVMOptions := []string{"-Dgtw.disable.exit.dialog=true"} vmoptions := deduplicateVMOption(ideaVMOptionsLines, gitpodVMOptions, filterFunc) - // user-defined vmoptions + // user-defined vmoptions (EnvVar) userVMOptionsVar := os.Getenv(strings.ToUpper(alias) + "_VMOPTIONS") userVMOptions := strings.Fields(userVMOptionsVar) if len(userVMOptions) > 0 { vmoptions = deduplicateVMOption(vmoptions, userVMOptions, filterFunc) } + + // project-defined vmoptions (.gitpod.yml) + if config != nil { + productConfig := getProductConfig(config, alias) + if productConfig != nil { + projectVMOptions := strings.Fields(productConfig.VMOptions) + if len(projectVMOptions) > 0 { + vmoptions = deduplicateVMOption(vmoptions, projectVMOptions, filterFunc) + } + } + } + // vmoptions file should end with a newline return strings.Join(vmoptions, "\n") + "\n" } @@ -410,8 +427,8 @@ func resolveBackendVersion() (*version.Version, error) { return version.NewVersion(info.Version) } -func installPlugins(wsInfo *supervisor.WorkspaceInfoResponse, alias string) error { - plugins, err := getPlugins(wsInfo.GetCheckoutLocation(), alias) +func installPlugins(repoRoot string, config *gitpod.GitpodConfig, alias string) error { + plugins, err := getPlugins(config, alias) if err != nil { return err } @@ -433,7 +450,7 @@ func installPlugins(wsInfo *supervisor.WorkspaceInfoResponse, alias string) erro var args []string args = append(args, "installPlugins") - args = append(args, wsInfo.GetCheckoutLocation()) + args = append(args, repoRoot) args = append(args, plugins...) cmd := remoteDevServerCmd(args) cmd.Stdout = io.MultiWriter(w, os.Stdout) @@ -458,7 +475,7 @@ func installPlugins(wsInfo *supervisor.WorkspaceInfoResponse, alias string) erro return nil } -func getPlugins(repoRoot string, alias string) ([]string, error) { +func parseGitpodConfig(repoRoot string) (*gitpod.GitpodConfig, error) { if repoRoot == "" { return nil, errors.New("repoRoot is empty") } @@ -474,11 +491,14 @@ func getPlugins(repoRoot string, alias string) ([]string, error) { if err = yaml.Unmarshal(data, &config); err != nil { return nil, errors.New("unmarshal .gitpod.yml file failed" + err.Error()) } + return config, nil +} +func getPlugins(config *gitpod.GitpodConfig, alias string) ([]string, error) { + var plugins []string if config == nil || config.JetBrains == nil { return nil, nil } - var plugins []string if config.JetBrains.Plugins != nil { plugins = append(plugins, config.JetBrains.Plugins...) } diff --git a/components/ide/jetbrains/image/status/main_test.go b/components/ide/jetbrains/image/status/main_test.go index 7d9c984e94c0e9..3757572ad3d110 100644 --- a/components/ide/jetbrains/image/status/main_test.go +++ b/components/ide/jetbrains/image/status/main_test.go @@ -11,6 +11,7 @@ import ( protocol "github.com/gitpod-io/gitpod/gitpod-protocol" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/assert" ) func TestGetProductConfig(t *testing.T) { @@ -26,6 +27,14 @@ func TestGetProductConfig(t *testing.T) { } } +func TestParseGitpodConfig(t *testing.T) { + gitpodConfig, _ := parseGitpodConfig("testdata") + assert.Equal(t, 1, len(gitpodConfig.JetBrains.IntelliJ.Plugins)) + assert.Equal(t, "both", gitpodConfig.JetBrains.IntelliJ.Prebuilds.Version) + assert.Equal(t, "-Xmx3g", gitpodConfig.JetBrains.IntelliJ.VMOptions) + assert.Equal(t, "-Xmx4096m -XX:MaxRAMPercentage=75", gitpodConfig.JetBrains.GoLand.VMOptions) +} + func TestUpdateVMOptions(t *testing.T) { tests := []struct { Desc string @@ -49,7 +58,7 @@ func TestUpdateVMOptions(t *testing.T) { lessFunc := func(a, b string) bool { return a < b } t.Run(test.Desc, func(t *testing.T) { - actual := updateVMOptions(test.Alias, test.Src) + actual := updateVMOptions(nil, test.Alias, test.Src) if diff := cmp.Diff(strings.Fields(test.Expectation), strings.Fields(actual), cmpopts.SortSlices(lessFunc)); diff != "" { t.Errorf("unexpected output (-want +got):\n%s", diff) } @@ -58,7 +67,7 @@ func TestUpdateVMOptions(t *testing.T) { t.Run("updateVMOptions multiple time should be stable", func(t *testing.T) { actual := test.Src for i := 0; i < 5; i++ { - actual = updateVMOptions(test.Alias, actual) + actual = updateVMOptions(nil, test.Alias, actual) if diff := cmp.Diff(strings.Fields(test.Expectation), strings.Fields(actual), cmpopts.SortSlices(lessFunc)); diff != "" { t.Errorf("unexpected output (-want +got):\n%s", diff) } diff --git a/components/ide/jetbrains/image/status/testdata/.gitpod.yml b/components/ide/jetbrains/image/status/testdata/.gitpod.yml new file mode 100644 index 00000000000000..d9a38521efa494 --- /dev/null +++ b/components/ide/jetbrains/image/status/testdata/.gitpod.yml @@ -0,0 +1,31 @@ +# Copyright (c) 2022 Gitpod GmbH. All rights reserved. +# Licensed under the GNU Affero General Public License (AGPL). +# See License-AGPL.txt in the project root for license information. + +# see https://github.com/gitpod-io/spring-petclinic/blob/master/.gitpod.yml +tasks: + - init: ./mvnw package -DskipTests + command: java -jar target/*.jar + name: Run PetClinic app + +# exposed ports +ports: + - port: 8080 + onOpen: open-preview + +vscode: + extensions: + - redhat.java + - vscjava.vscode-java-debug + - vscjava.vscode-java-test + - pivotal.vscode-spring-boot + +jetbrains: + intellij: + plugins: + - com.haulmont.jpab + prebuilds: + version: both + vmoptions: "-Xmx3g" + goland: + vmoptions: "-Xmx4096m -XX:MaxRAMPercentage=75"