From 19363d2b1a33d068f2a71a48972a18d7614ef79a Mon Sep 17 00:00:00 2001 From: Deepak Bhatt Date: Tue, 7 Jan 2025 13:10:54 +0000 Subject: [PATCH] feat: [CDE-576]: add devcontainer config for Jetbrains (#3215) * feat: [CDE-576]: fix hardcoding * feat: [CDE-576]: fix lint * feat: [CDE-576]: lint * feat: [CDE-576]: make IDE path configurable * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into CDE-576-devcontainer-json-intellij * feat: [CDE-576]: fix script name * feat: [CDE-576]: configure Jetbrains IDEs * feat: [CDE-576]: change to intellij ide type * feat: [CDE-576]: fix comments * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into CDE-576-devcontainer-json-intellij * feat: [CDE-576]: add devcontainer config for Jetbrains * feat: [CDE-552]: fix lint * feat: [CDE-552]: wait for the ide to run * feat: [CDE-552}: fix lint * feat: [CDE-552}: address comments * feat: [CDE-552}: add support for arm and amd architecture * feat: [CDE-552}: fix lint * feat: [CDE-552}: fix build * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into CDE-563-intellij-support * feat: [CDE-552}: fix installation for intellij * feat: [CDE-552}: fix installation for intellij * feat: [CDE-552}: add install tools, setup and run scripts for intellij * add intellij support # Conflicts: # app/gitspace/infrastructure/trigger_infra_event.go # app/gitspace/orchestrator/utils/script_templates/setup_intellij.sh --- app/gitspace/infrastructure/find.go | 5 +- .../container/devcontainer_config_utils.go | 42 +++-- .../embedded_docker_container_orchestrator.go | 2 +- app/gitspace/orchestrator/ide/common.go | 10 +- app/gitspace/orchestrator/ide/factory.go | 15 +- .../ide/{intellij.go => jetbrains.go} | 80 +++++----- app/gitspace/orchestrator/ide/wire.go | 21 ++- .../{run_intellij.sh => run_jetbrains_ide.sh} | 0 ...tup_intellij.sh => setup_jetbrains_ide.sh} | 0 app/gitspace/orchestrator/utils/tools.go | 18 ++- app/gitspace/types/types.go | 11 +- cli/operations/server/config.go | 6 +- cmd/gitness/wire_gen.go | 10 +- types/devcontainer_config_customizations.go | 147 ++++++++++++++++-- types/enum/ide.go | 33 +++- types/enum/intellij.go | 21 --- types/{intellij.go => ide.go} | 8 +- types/jetbrains.go | 81 ++++++++++ 18 files changed, 387 insertions(+), 123 deletions(-) rename app/gitspace/orchestrator/ide/{intellij.go => jetbrains.go} (74%) rename app/gitspace/orchestrator/utils/script_templates/{run_intellij.sh => run_jetbrains_ide.sh} (100%) rename app/gitspace/orchestrator/utils/script_templates/{setup_intellij.sh => setup_jetbrains_ide.sh} (100%) delete mode 100644 types/enum/intellij.go rename types/{intellij.go => ide.go} (86%) create mode 100644 types/jetbrains.go diff --git a/app/gitspace/infrastructure/find.go b/app/gitspace/infrastructure/find.go index 602328418..eb3f079f3 100644 --- a/app/gitspace/infrastructure/find.go +++ b/app/gitspace/infrastructure/find.go @@ -130,8 +130,9 @@ func getGitspaceScheme(ideType enum.IDEType, gitspaceSchemeFromMetadata string) return gitspaceSchemeFromMetadata, nil case enum.IDETypeVSCode: return "ssh", nil - case enum.IDETypeIntellij: - return gitspaceSchemeFromMetadata, nil + case enum.IDETypeIntelliJ, enum.IDETypePyCharm, enum.IDETypeGoland, enum.IDETypeWebStorm, enum.IDETypeCLion, + enum.IDETypePHPStorm, enum.IDETypeRubyMine, enum.IDETypeRider: + return "ssh", nil default: return "", fmt.Errorf("unknown ideType %s", ideType) } diff --git a/app/gitspace/orchestrator/container/devcontainer_config_utils.go b/app/gitspace/orchestrator/container/devcontainer_config_utils.go index 5cf2424a7..fc9f7fff2 100644 --- a/app/gitspace/orchestrator/container/devcontainer_config_utils.go +++ b/app/gitspace/orchestrator/container/devcontainer_config_utils.go @@ -200,9 +200,17 @@ func AddIDECustomizationsArg( devcontainerConfig types.DevcontainerConfig, args map[gitspaceTypes.IDEArg]interface{}, ) map[gitspaceTypes.IDEArg]interface{} { - if ideService.Type() == enum.IDETypeVSCodeWeb || ideService.Type() == enum.IDETypeVSCode { - if devcontainerConfig.Customizations.ExtractVSCodeSpec() != nil { - args[gitspaceTypes.VSCodeCustomizationArg] = *devcontainerConfig.Customizations.ExtractVSCodeSpec() + switch ideService.Type() { + case enum.IDETypeVSCodeWeb, enum.IDETypeVSCode: + vscodeSpecs := devcontainerConfig.Customizations.ExtractVSCodeSpec() + if vscodeSpecs != nil { + args[gitspaceTypes.VSCodeCustomizationArg] = *vscodeSpecs + } + case enum.IDETypeIntelliJ, enum.IDETypePyCharm, enum.IDETypeGoland, enum.IDETypeWebStorm, enum.IDETypeCLion, + enum.IDETypePHPStorm, enum.IDETypeRubyMine, enum.IDETypeRider: + jetbrainsSpecs := devcontainerConfig.Customizations.ExtractJetBrainsSpecs() + if jetbrainsSpecs != nil { + args[gitspaceTypes.JetBrainsCustomizationArg] = *jetbrainsSpecs } } return args @@ -212,11 +220,18 @@ func AddIDEDownloadURLArg( ideService ide.IDE, args map[gitspaceTypes.IDEArg]interface{}, ) map[gitspaceTypes.IDEArg]interface{} { - if ideService.Type() == enum.IDETypeIntellij { - args[gitspaceTypes.IDEDownloadURLArg] = types.IntellijDownloadURL{ - Arm64: fmt.Sprintf(enum.IDEIntellijDownloadURLArm64Template, enum.IDEIntellijVer), - Amd64: fmt.Sprintf(enum.IDEIntellijDownloadURLAmd64Template, enum.IDEIntellijVer), - } + if !enum.IsJetBrainsIDE(ideService.Type()) { + // currently download url is only need for jetbrains IDEs + return args + } + + ideType := ideService.Type() + ideDownloadURLTemplate := types.JetBrainsIDEDownloadURLTemplateMap[ideType] + args[gitspaceTypes.IDEDownloadURLArg] = types.IDEDownloadURLs{ + Amd64Sha: ideDownloadURLTemplate.Amd64Sha, + Arm64Sha: ideDownloadURLTemplate.Arm64Sha, + Amd64: fmt.Sprintf(ideDownloadURLTemplate.Amd64, ideDownloadURLTemplate.Version), + Arm64: fmt.Sprintf(ideDownloadURLTemplate.Arm64, ideDownloadURLTemplate.Version), } return args @@ -226,10 +241,15 @@ func AddIDEDirNameArg( ideService ide.IDE, args map[gitspaceTypes.IDEArg]interface{}, ) map[gitspaceTypes.IDEArg]interface{} { - if ideService.Type() == enum.IDETypeIntellij { - dirname := path.Join(".cache", "JetBrains", "RemoteDev", "dist", "intellij") - args[gitspaceTypes.IDEDIRNameArg] = dirname + if !enum.IsJetBrainsIDE(ideService.Type()) { + // currently dirname is only need for jetbrains IDEs + return args } + ideType := ideService.Type() + + dirname := path.Join(".cache", "JetBrains", "RemoteDev", "dist", ideType.String()) + args[gitspaceTypes.IDEDIRNameArg] = dirname + return args } diff --git a/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go b/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go index 8fca3abac..d0abf7d63 100644 --- a/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go +++ b/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go @@ -612,7 +612,7 @@ func (e *EmbeddedDockerOrchestrator) buildSetupSteps( } } -// setupGitspaceAndIDE initializes Gitspace and IDE by registering and executing the setup steps. +// setupGitspaceAndIDE initializes Gitspace and IdeType by registering and executing the setup steps. func (e *EmbeddedDockerOrchestrator) setupGitspaceAndIDE( ctx context.Context, exec *devcontainer.Exec, diff --git a/app/gitspace/orchestrator/ide/common.go b/app/gitspace/orchestrator/ide/common.go index bd140086e..1adb32c12 100644 --- a/app/gitspace/orchestrator/ide/common.go +++ b/app/gitspace/orchestrator/ide/common.go @@ -23,18 +23,18 @@ import ( func getIDEDownloadURL( args map[gitspaceTypes.IDEArg]interface{}, -) (types.IntellijDownloadURL, error) { +) (types.IDEDownloadURLs, error) { downloadURL, exists := args[gitspaceTypes.IDEDownloadURLArg] if !exists { - return types.IntellijDownloadURL{}, fmt.Errorf("ide download url not found") + return types.IDEDownloadURLs{}, fmt.Errorf("ide download url not found") } - downloadURLStr, ok := downloadURL.(types.IntellijDownloadURL) + downloadURLs, ok := downloadURL.(types.IDEDownloadURLs) if !ok { - return types.IntellijDownloadURL{}, fmt.Errorf("ide download url is not of type IntellijDownloadURL") + return types.IDEDownloadURLs{}, fmt.Errorf("ide download url is not of type JetBrainsSpecs") } - return downloadURLStr, nil + return downloadURLs, nil } func getIDEDirName(args map[gitspaceTypes.IDEArg]interface{}) (string, error) { diff --git a/app/gitspace/orchestrator/ide/factory.go b/app/gitspace/orchestrator/ide/factory.go index 97e6f127a..b00f7b18e 100644 --- a/app/gitspace/orchestrator/ide/factory.go +++ b/app/gitspace/orchestrator/ide/factory.go @@ -24,11 +24,22 @@ type Factory struct { ides map[enum.IDEType]IDE } -func NewFactory(vscode *VSCode, vscodeWeb *VSCodeWeb, intellij *Intellij) Factory { +func NewFactory( + vscode *VSCode, + vscodeWeb *VSCodeWeb, + jetBrainsIDEsMap map[enum.IDEType]*JetBrainsIDE, +) Factory { ides := make(map[enum.IDEType]IDE) ides[enum.IDETypeVSCode] = vscode ides[enum.IDETypeVSCodeWeb] = vscodeWeb - ides[enum.IDETypeIntellij] = intellij + ides[enum.IDETypeIntelliJ] = jetBrainsIDEsMap[enum.IDETypeIntelliJ] + ides[enum.IDETypePyCharm] = jetBrainsIDEsMap[enum.IDETypePyCharm] + ides[enum.IDETypeGoland] = jetBrainsIDEsMap[enum.IDETypeGoland] + ides[enum.IDETypeWebStorm] = jetBrainsIDEsMap[enum.IDETypeWebStorm] + ides[enum.IDETypeCLion] = jetBrainsIDEsMap[enum.IDETypeCLion] + ides[enum.IDETypePHPStorm] = jetBrainsIDEsMap[enum.IDETypePHPStorm] + ides[enum.IDETypeRubyMine] = jetBrainsIDEsMap[enum.IDETypeRubyMine] + ides[enum.IDETypeRider] = jetBrainsIDEsMap[enum.IDETypeRider] return Factory{ides: ides} } diff --git a/app/gitspace/orchestrator/ide/intellij.go b/app/gitspace/orchestrator/ide/jetbrains.go similarity index 74% rename from app/gitspace/orchestrator/ide/intellij.go rename to app/gitspace/orchestrator/ide/jetbrains.go index eb70cf1f0..9341e4772 100644 --- a/app/gitspace/orchestrator/ide/intellij.go +++ b/app/gitspace/orchestrator/ide/jetbrains.go @@ -28,54 +28,58 @@ import ( "github.com/harness/gitness/types/enum" ) -var _ IDE = (*Intellij)(nil) +var _ IDE = (*JetBrainsIDE)(nil) const ( - templateSetupIntellij string = "setup_intellij.sh" - templateRunRemoteIDEIntellij string = "run_intellij.sh" + templateSetupJetBrainsIDE string = "setup_jetbrains_ide.sh" + templateRunRemoteJetBrainsIDE string = "run_jetbrains_ide.sh" intellijURLScheme string = "jetbrains-gateway" ) -type IntellijConfig struct { +type JetBrainsIDEConfig struct { Port int } -type Intellij struct { - config IntellijConfig +type JetBrainsIDE struct { + ideType enum.IDEType + config JetBrainsIDEConfig } -func NewIntellijService(config *IntellijConfig) *Intellij { - return &Intellij{config: *config} +func NewJetBrainsIDEService(config *JetBrainsIDEConfig, ideType enum.IDEType) *JetBrainsIDE { + return &JetBrainsIDE{ + ideType: ideType, + config: *config, + } } // Setup installs the SSH server inside the container. -func (ij *Intellij) Setup( +func (jb *JetBrainsIDE) Setup( ctx context.Context, exec *devcontainer.Exec, args map[gitspaceTypes.IDEArg]interface{}, gitspaceLogger gitspaceTypes.GitspaceLogger, ) error { gitspaceLogger.Info("Installing ssh-server inside container") - err := ij.setupSSHServer(ctx, exec, gitspaceLogger) + err := jb.setupSSHServer(ctx, exec, gitspaceLogger) if err != nil { return fmt.Errorf("failed to setup SSH server: %w", err) } gitspaceLogger.Info("Successfully installed ssh-server") - gitspaceLogger.Info("Installing intelliJ IDE inside container") + gitspaceLogger.Info(fmt.Sprintf("Installing %s IdeType inside container...", jb.ideType)) gitspaceLogger.Info("IDE setup output...") - err = ij.setupIntellijIDE(ctx, exec, args, gitspaceLogger) + err = jb.setupIntellijIDE(ctx, exec, args, gitspaceLogger) if err != nil { - return fmt.Errorf("failed to setup IntelliJ IDE: %w", err) + return fmt.Errorf("failed to setup %s IdeType: %w", jb.ideType, err) } - gitspaceLogger.Info("Successfully installed IntelliJ IDE") + gitspaceLogger.Info(fmt.Sprintf("Successfully installed %s IdeType", jb.ideType)) gitspaceLogger.Info("Successfully set up IDE inside container") return nil } -func (ij *Intellij) setupSSHServer( +func (jb *JetBrainsIDE) setupSSHServer( ctx context.Context, exec *devcontainer.Exec, gitspaceLogger gitspaceTypes.GitspaceLogger, @@ -100,7 +104,7 @@ func (ij *Intellij) setupSSHServer( return nil } -func (ij *Intellij) setupIntellijIDE( +func (jb *JetBrainsIDE) setupIntellijIDE( ctx context.Context, exec *devcontainer.Exec, args map[gitspaceTypes.IDEArg]interface{}, @@ -126,11 +130,11 @@ func (ij *Intellij) setupIntellijIDE( payload.IdeDirName = dirName intellijIDEScript, err := utils.GenerateScriptFromTemplate( - templateSetupIntellij, &payload) + templateSetupJetBrainsIDE, &payload) if err != nil { return fmt.Errorf( "failed to generate scipt to setup intellij idea from template %s: %w", - templateSetupIntellij, + templateSetupJetBrainsIDE, err, ) } @@ -138,43 +142,43 @@ func (ij *Intellij) setupIntellijIDE( err = exec.ExecuteCommandInHomeDirAndLog(ctx, intellijIDEScript, false, gitspaceLogger, true) if err != nil { - return fmt.Errorf("failed to setup intellij IDE: %w", err) + return fmt.Errorf("failed to setup intellij IdeType: %w", err) } return nil } // Run runs the SSH server inside the container. -func (ij *Intellij) Run( +func (jb *JetBrainsIDE) Run( ctx context.Context, exec *devcontainer.Exec, args map[gitspaceTypes.IDEArg]interface{}, gitspaceLogger gitspaceTypes.GitspaceLogger, ) error { gitspaceLogger.Info("SSH server run output...") - err := ij.runSSHServer(ctx, exec, args, gitspaceLogger) + err := jb.runSSHServer(ctx, exec, args, gitspaceLogger) if err != nil { return err } gitspaceLogger.Info("Successfully run ssh-server") - gitspaceLogger.Info("Run Remote IntelliJ IDE...") - err = ij.runRemoteIDE(ctx, exec, args, gitspaceLogger) + gitspaceLogger.Info("Run Remote IntelliJ IdeType...") + err = jb.runRemoteIDE(ctx, exec, args, gitspaceLogger) if err != nil { return err } - gitspaceLogger.Info("Successfully Run Remote IntelliJ IDE") + gitspaceLogger.Info("Successfully Run Remote IntelliJ IdeType") return nil } -func (ij *Intellij) runSSHServer( +func (jb *JetBrainsIDE) runSSHServer( ctx context.Context, exec *devcontainer.Exec, _ map[gitspaceTypes.IDEArg]interface{}, gitspaceLogger gitspaceTypes.GitspaceLogger, ) error { payload := gitspaceTypes.RunSSHServerPayload{ - Port: strconv.Itoa(ij.config.Port), + Port: strconv.Itoa(jb.config.Port), } runSSHScript, err := utils.GenerateScriptFromTemplate( templateRunSSHServer, &payload) @@ -192,7 +196,7 @@ func (ij *Intellij) runSSHServer( return nil } -func (ij *Intellij) runRemoteIDE( +func (jb *JetBrainsIDE) runRemoteIDE( ctx context.Context, exec *devcontainer.Exec, args map[gitspaceTypes.IDEArg]interface{}, @@ -216,32 +220,36 @@ func (ij *Intellij) runRemoteIDE( payload.IdeDirName = dirName runSSHScript, err := utils.GenerateScriptFromTemplate( - templateRunRemoteIDEIntellij, &payload) + templateRunRemoteJetBrainsIDE, &payload) if err != nil { return fmt.Errorf( - "failed to generate scipt to run intelliJ IDE from template %s: %w", templateRunSSHServer, err) + "failed to generate scipt to run intelliJ IdeType from template %s: %w", templateRunSSHServer, err) } err = exec.ExecuteCommandInHomeDirAndLog(ctx, runSSHScript, false, gitspaceLogger, true) if err != nil { - return fmt.Errorf("failed to run intelliJ IDE: %w", err) + return fmt.Errorf("failed to run intelliJ IdeType: %w", err) } return nil } // Port returns the port on which the ssh-server is listening. -func (ij *Intellij) Port() *types.GitspacePort { +func (jb *JetBrainsIDE) Port() *types.GitspacePort { return &types.GitspacePort{ - Port: ij.config.Port, + Port: jb.config.Port, Protocol: enum.CommunicationProtocolSSH, } } +func (jb *JetBrainsIDE) Type() enum.IDEType { + return jb.ideType +} + // GenerateURL returns the url to redirect user to ide(here to jetbrains gateway application). -func (ij *Intellij) GenerateURL(absoluteRepoPath, host, port, user string) string { +func (jb *JetBrainsIDE) GenerateURL(absoluteRepoPath, host, port, user string) string { homePath := getHomePath(absoluteRepoPath) - idePath := path.Join(homePath, ".cache", "JetBrains", "RemoteDev", "dist", "intellij") + idePath := path.Join(homePath, ".cache", "JetBrains", "RemoteDev", "dist", jb.ideType.String()) ideURL := url.URL{ Scheme: intellijURLScheme, Host: "", // Empty since we include the host and port in the path @@ -259,7 +267,3 @@ func (ij *Intellij) GenerateURL(absoluteRepoPath, host, port, user string) strin return ideURL.String() } - -func (ij *Intellij) Type() enum.IDEType { - return enum.IDETypeIntellij -} diff --git a/app/gitspace/orchestrator/ide/wire.go b/app/gitspace/orchestrator/ide/wire.go index 5e19e48d5..58c9b41a0 100644 --- a/app/gitspace/orchestrator/ide/wire.go +++ b/app/gitspace/orchestrator/ide/wire.go @@ -15,13 +15,15 @@ package ide import ( + "github.com/harness/gitness/types/enum" + "github.com/google/wire" ) var WireSet = wire.NewSet( ProvideVSCodeWebService, ProvideVSCodeService, - ProvideIntellijService, + ProvideJetBrainsIDEsService, ProvideIDEFactory, ) @@ -33,14 +35,23 @@ func ProvideVSCodeService(config *VSCodeConfig) *VSCode { return NewVsCodeService(config) } -func ProvideIntellijService(config *IntellijConfig) *Intellij { - return NewIntellijService(config) +func ProvideJetBrainsIDEsService(config *JetBrainsIDEConfig) map[enum.IDEType]*JetBrainsIDE { + return map[enum.IDEType]*JetBrainsIDE{ + enum.IDETypeIntelliJ: NewJetBrainsIDEService(config, enum.IDETypeIntelliJ), + enum.IDETypePyCharm: NewJetBrainsIDEService(config, enum.IDETypePyCharm), + enum.IDETypeGoland: NewJetBrainsIDEService(config, enum.IDETypeGoland), + enum.IDETypeWebStorm: NewJetBrainsIDEService(config, enum.IDETypeWebStorm), + enum.IDETypeCLion: NewJetBrainsIDEService(config, enum.IDETypeCLion), + enum.IDETypePHPStorm: NewJetBrainsIDEService(config, enum.IDETypePHPStorm), + enum.IDETypeRubyMine: NewJetBrainsIDEService(config, enum.IDETypeRubyMine), + enum.IDETypeRider: NewJetBrainsIDEService(config, enum.IDETypeRider), + } } func ProvideIDEFactory( vscode *VSCode, vscodeWeb *VSCodeWeb, - intellij *Intellij, + jetBrainsIDEsMap map[enum.IDEType]*JetBrainsIDE, ) Factory { - return NewFactory(vscode, vscodeWeb, intellij) + return NewFactory(vscode, vscodeWeb, jetBrainsIDEsMap) } diff --git a/app/gitspace/orchestrator/utils/script_templates/run_intellij.sh b/app/gitspace/orchestrator/utils/script_templates/run_jetbrains_ide.sh similarity index 100% rename from app/gitspace/orchestrator/utils/script_templates/run_intellij.sh rename to app/gitspace/orchestrator/utils/script_templates/run_jetbrains_ide.sh diff --git a/app/gitspace/orchestrator/utils/script_templates/setup_intellij.sh b/app/gitspace/orchestrator/utils/script_templates/setup_jetbrains_ide.sh similarity index 100% rename from app/gitspace/orchestrator/utils/script_templates/setup_intellij.sh rename to app/gitspace/orchestrator/utils/script_templates/setup_jetbrains_ide.sh diff --git a/app/gitspace/orchestrator/utils/tools.go b/app/gitspace/orchestrator/utils/tools.go index 521dffe42..74e7c0482 100644 --- a/app/gitspace/orchestrator/utils/tools.go +++ b/app/gitspace/orchestrator/utils/tools.go @@ -42,8 +42,9 @@ func InstallTools( return err } return nil - case enum.IDETypeIntellij: - err := InstallToolsForIntellij(ctx, exec, gitspaceLogger) + case enum.IDETypeIntelliJ, enum.IDETypePyCharm, enum.IDETypeGoland, enum.IDETypeWebStorm, enum.IDETypeCLion, + enum.IDETypePHPStorm, enum.IDETypeRubyMine, enum.IDETypeRider: + err := InstallToolsForJetBrains(ctx, exec, ideType, gitspaceLogger) if err != nil { return err } @@ -101,9 +102,10 @@ func InstallToolsForVsCode( return nil } -func InstallToolsForIntellij( +func InstallToolsForJetBrains( ctx context.Context, exec *devcontainer.Exec, + ideType enum.IDEType, gitspaceLogger types.GitspaceLogger, ) error { script, err := GenerateScriptFromTemplate( @@ -112,15 +114,15 @@ func InstallToolsForIntellij( }) if err != nil { return fmt.Errorf( - "failed to generate scipt to install tools for intellij from template %s: %w", - templateIntellijToolsInstallation, err) + "failed to generate scipt to install tools for %s from template %s: %w", + ideType, templateIntellijToolsInstallation, err) } - gitspaceLogger.Info("Installing tools for intellij in container") + gitspaceLogger.Info(fmt.Sprintf("Installing tools for %s in container", ideType)) err = exec.ExecuteCommandInHomeDirAndLog(ctx, script, true, gitspaceLogger, false) if err != nil { - return fmt.Errorf("failed to install tools for intellij: %w", err) + return fmt.Errorf("failed to install tools for %s: %w", ideType, err) } - gitspaceLogger.Info("Successfully installed tools for intellij") + gitspaceLogger.Info(fmt.Sprintf("Successfully installed tools for %s in container", ideType)) return nil } diff --git a/app/gitspace/types/types.go b/app/gitspace/types/types.go index af387a748..13511baab 100644 --- a/app/gitspace/types/types.go +++ b/app/gitspace/types/types.go @@ -23,11 +23,12 @@ import ( type IDEArg string const ( - VSCodeCustomizationArg IDEArg = "VSCODE_CUSTOMIZATION" - VSCodeProxyURIArg IDEArg = "VSCODE_PROXY_URI" - IDERepoNameArg IDEArg = "IDE_REPO_NAME" - IDEDownloadURLArg IDEArg = "IDE_DOWNLOAD_URL" - IDEDIRNameArg IDEArg = "IDE_DIR_NAME" + VSCodeCustomizationArg IDEArg = "VSCODE_CUSTOMIZATION" + JetBrainsCustomizationArg IDEArg = "JETBRAINS_CUSTOMIZATION" + VSCodeProxyURIArg IDEArg = "VSCODE_PROXY_URI" + IDERepoNameArg IDEArg = "IDE_REPO_NAME" + IDEDownloadURLArg IDEArg = "IDE_DOWNLOAD_URL" + IDEDIRNameArg IDEArg = "IDE_DIR_NAME" ) type GitspaceLogger interface { diff --git a/cli/operations/server/config.go b/cli/operations/server/config.go index ab08e40bf..9bf96847a 100644 --- a/cli/operations/server/config.go +++ b/cli/operations/server/config.go @@ -442,9 +442,9 @@ func ProvideIDEVSCodeConfig(config *types.Config) *ide.VSCodeConfig { } } -// ProvideIDEIntellijConfig loads the Intellij IDE config from the main config. -func ProvideIDEIntellijConfig(config *types.Config) *ide.IntellijConfig { - return &ide.IntellijConfig{ +// ProvideIDEIntellijConfig loads the IdeType IDE config from the main config. +func ProvideIDEIntellijConfig(config *types.Config) *ide.JetBrainsIDEConfig { + return &ide.JetBrainsIDEConfig{ Port: config.IDE.Intellij.Port, } } diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index 3b6663cc8..6b30082ba 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -333,9 +333,9 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro vsCode := ide.ProvideVSCodeService(vsCodeConfig) vsCodeWebConfig := server.ProvideIDEVSCodeWebConfig(config) vsCodeWeb := ide.ProvideVSCodeWebService(vsCodeWebConfig) - intellijConfig := server.ProvideIDEIntellijConfig(config) - intellij := ide.ProvideIntellijService(intellijConfig) - ideFactory := ide.ProvideIDEFactory(vsCode, vsCodeWeb, intellij) + jetBrainsIDEConfig := server.ProvideIDEIntellijConfig(config) + v := ide.ProvideJetBrainsIDEsService(jetBrainsIDEConfig) + ideFactory := ide.ProvideIDEFactory(vsCode, vsCodeWeb, v) passwordResolver := secret.ProvidePasswordResolver() resolverFactory := secret.ProvideResolverFactory(passwordResolver) orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, platformConnector, infraProvisioner, containerOrchestrator, eventsReporter, orchestratorConfig, ideFactory, resolverFactory) @@ -409,8 +409,8 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro serviceaccountController := serviceaccount.NewController(principalUID, authorizer, principalStore, spaceStore, repoStore, tokenStore) principalController := principal.ProvideController(principalStore, authorizer) usergroupController := usergroup2.ProvideController(userGroupStore, spaceStore, authorizer, searchService) - v := check2.ProvideCheckSanitizers() - checkController := check2.ProvideController(transactor, authorizer, spaceStore, checkStore, spaceCache, repoFinder, gitInterface, v, streamer) + v2 := check2.ProvideCheckSanitizers() + checkController := check2.ProvideController(transactor, authorizer, spaceStore, checkStore, spaceCache, repoFinder, gitInterface, v2, streamer) systemController := system.NewController(principalStore, config) blobConfig, err := server.ProvideBlobStoreConfig(config) if err != nil { diff --git a/types/devcontainer_config_customizations.go b/types/devcontainer_config_customizations.go index 486180964..d07e8d814 100644 --- a/types/devcontainer_config_customizations.go +++ b/types/devcontainer_config_customizations.go @@ -17,12 +17,15 @@ package types import ( "encoding/json" + "github.com/harness/gitness/types/enum" + "github.com/rs/zerolog/log" ) const ( - GitspaceCustomizationsKey CustomizationsKey = "harnessGitspaces" - VSCodeCustomizationsKey CustomizationsKey = "vscode" + GitspaceCustomizationsKey CustomizationsKey = "harnessGitspaces" + VSCodeCustomizationsKey CustomizationsKey = "vscode" + JetBrainsCustomizationsKey CustomizationsKey = "jetbrains" ) type CustomizationsKey string @@ -31,8 +34,116 @@ func (ck CustomizationsKey) String() string { return string(ck) } +// DevContainerConfigCustomizations implements various Extract* function to extract out custom field defines in +// customization field in devcontainer.json. type DevContainerConfigCustomizations map[string]interface{} +// VSCodeCustomizationSpecs contains details about vscode customization. +// eg: +// +// "customizations": { +// // Configure properties specific to VS Code. +// "vscode": { +// "settings": { +// "java.home": "/docker-java-home" +// }, +// "extensions": [ +// "streetsidesoftware.code-spell-checker" +// ] +// } +// } +type VSCodeCustomizationSpecs struct { + Extensions []string `json:"extensions"` + Settings map[string]interface{} `json:"settings"` +} + +// GitspaceCustomizationSpecs contains details about harness platform connectors. +// eg: +// +// "customizations": { +// "harnessGitspaces": { +// "connectors": [ +// { +// "type": "DockerRegistry", +// "identifier": "testharnessjfrog" +// }, +// { +// "type": "Artifactory", +// "identifier": "testartifactoryconnector" +// } +// ] +// } +// } +type GitspaceCustomizationSpecs struct { + Connectors []struct { + Type string `json:"type"` + ID string `json:"identifier"` + } `json:"connectors"` +} + +type JetBrainsBackend string + +func (jb JetBrainsBackend) String() string { + return string(jb) +} + +func (jb JetBrainsBackend) Valid() bool { + _, valid := ValidJetBrainsBackendSet[jb] + + return valid +} + +func (jb JetBrainsBackend) IdeType() enum.IDEType { + var ideType enum.IDEType + switch jb { + case IntelliJJetBrainsBackend: + ideType = enum.IDETypeIntelliJ + case GolandJetBrainsBackend: + ideType = enum.IDETypeGoland + case PyCharmJetBrainsBackend: + ideType = enum.IDETypePyCharm + case WebStormJetBrainsBackend: + ideType = enum.IDETypeWebStorm + case CLionJetBrainsBackend: + ideType = enum.IDETypeCLion + case PhpStormJetBrainsBackend: + ideType = enum.IDETypePHPStorm + case RubyMineJetBrainsBackend: + ideType = enum.IDETypeRubyMine + case RiderJetBrainsBackend: + ideType = enum.IDETypeRider + } + + return ideType +} + +const ( + IntelliJJetBrainsBackend JetBrainsBackend = "IntelliJ" + GolandJetBrainsBackend JetBrainsBackend = "Goland" + PyCharmJetBrainsBackend JetBrainsBackend = "PyCharm" + WebStormJetBrainsBackend JetBrainsBackend = "WebStorm" + CLionJetBrainsBackend JetBrainsBackend = "CLion" + PhpStormJetBrainsBackend JetBrainsBackend = "PhpStorm" + RubyMineJetBrainsBackend JetBrainsBackend = "RubyMine" + RiderJetBrainsBackend JetBrainsBackend = "Rider" +) + +var ValidJetBrainsBackendSet = map[JetBrainsBackend]struct{}{ + IntelliJJetBrainsBackend: {}, + GolandJetBrainsBackend: {}, + PyCharmJetBrainsBackend: {}, + WebStormJetBrainsBackend: {}, + CLionJetBrainsBackend: {}, + PhpStormJetBrainsBackend: {}, + RubyMineJetBrainsBackend: {}, + RiderJetBrainsBackend: {}, +} + +type JetBrainsCustomizationSpecs struct { + Backend JetBrainsBackend `json:"backend"` + Plugins []string `json:"plugins"` +} + func (dcc DevContainerConfigCustomizations) ExtractGitspaceSpec() *GitspaceCustomizationSpecs { val, ok := dcc[GitspaceCustomizationsKey.String()] if !ok { @@ -84,14 +195,28 @@ func (dcc DevContainerConfigCustomizations) ExtractVSCodeSpec() *VSCodeCustomiza return &vsCodeCustomizationSpecs } -type VSCodeCustomizationSpecs struct { - Extensions []string `json:"extensions"` - Settings map[string]interface{} `json:"settings"` -} +func (dcc DevContainerConfigCustomizations) ExtractJetBrainsSpecs() *JetBrainsCustomizationSpecs { + data, ok := dcc[JetBrainsCustomizationsKey.String()] + if !ok { + // Log that the key is missing, but return nil + log.Warn().Msgf("JetBrains customization key %q not found, returning empty struct", + JetBrainsCustomizationsKey) + return nil + } -type GitspaceCustomizationSpecs struct { - Connectors []struct { - Type string `json:"type"` - ID string `json:"identifier"` - } `json:"connectors"` + rawData, err := json.Marshal(data) + if err != nil { + // Log the error during marshalling and return nil + log.Printf("Failed to marshal data for key %q: %v", JetBrainsCustomizationsKey, err) + return nil + } + + var jetbrainsSpecs JetBrainsCustomizationSpecs + if err := json.Unmarshal(rawData, &jetbrainsSpecs); err != nil { + // Log the error during unmarshalling and return nil + log.Printf("Failed to unmarshal data for key %q: %v", JetBrainsCustomizationsKey, err) + return nil + } + + return &jetbrainsSpecs } diff --git a/types/enum/ide.go b/types/enum/ide.go index a1fe8cdc5..3cf869cd2 100644 --- a/types/enum/ide.go +++ b/types/enum/ide.go @@ -16,12 +16,39 @@ package enum type IDEType string -func (IDEType) Enum() []interface{} { return toInterfaceSlice(ideTypes) } +func (i IDEType) Enum() []interface{} { return toInterfaceSlice(ideTypes) } -var ideTypes = []IDEType{IDETypeVSCode, IDETypeVSCodeWeb, IDETypeIntellij} +func (i IDEType) String() string { return string(i) } + +var ideTypes = []IDEType{IDETypeVSCode, IDETypeVSCodeWeb, IDETypeIntelliJ, IDETypePyCharm, IDETypeGoland, + IDETypeWebStorm, IDETypeCLion, IDETypePHPStorm, IDETypeRubyMine, IDETypeRider} + +var jetBrainsIDESet = map[IDEType]struct{}{ + IDETypeIntelliJ: {}, + IDETypePyCharm: {}, + IDETypeGoland: {}, + IDETypeWebStorm: {}, + IDETypeCLion: {}, + IDETypePHPStorm: {}, + IDETypeRubyMine: {}, + IDETypeRider: {}, +} const ( IDETypeVSCode IDEType = "vs_code" IDETypeVSCodeWeb IDEType = "vs_code_web" - IDETypeIntellij IDEType = "intellij" + // all jetbrains IDEs. + IDETypeIntelliJ IDEType = "intellij" + IDETypePyCharm IDEType = "pycharm" + IDETypeGoland IDEType = "goland" + IDETypeWebStorm IDEType = "webstorm" + IDETypeCLion IDEType = "clion" + IDETypePHPStorm IDEType = "phpstorm" + IDETypeRubyMine IDEType = "rubymine" + IDETypeRider IDEType = "rider" ) + +func IsJetBrainsIDE(t IDEType) bool { + _, exist := jetBrainsIDESet[t] + return exist +} diff --git a/types/enum/intellij.go b/types/enum/intellij.go deleted file mode 100644 index 8e8a2a26b..000000000 --- a/types/enum/intellij.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023 Harness, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package enum - -const ( - IDEIntellijDownloadURLAmd64Template string = "https://download.jetbrains.com/idea/ideaIU-%s.tar.gz" - IDEIntellijDownloadURLArm64Template string = "https://download.jetbrains.com/idea/ideaIU-%s-aarch64.tar.gz" - IDEIntellijVer string = "2024.3" -) diff --git a/types/intellij.go b/types/ide.go similarity index 86% rename from types/intellij.go rename to types/ide.go index 57416369e..983f3a5d4 100644 --- a/types/intellij.go +++ b/types/ide.go @@ -14,7 +14,9 @@ package types -type IntellijDownloadURL struct { - Arm64 string - Amd64 string +type IDEDownloadURLs struct { + Arm64Sha string + Amd64Sha string + Arm64 string + Amd64 string } diff --git a/types/jetbrains.go b/types/jetbrains.go new file mode 100644 index 000000000..2d0f1b607 --- /dev/null +++ b/types/jetbrains.go @@ -0,0 +1,81 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import "github.com/harness/gitness/types/enum" + +type JetBrainsIDEDownloadURLTemplates struct { + Version string + Amd64Sha string + Arm64Sha string + Amd64 string + Arm64 string +} + +var JetBrainsIDEDownloadURLTemplateMap = map[enum.IDEType]JetBrainsIDEDownloadURLTemplates{ + enum.IDETypeIntelliJ: { + // list of versions: https://www.jetbrains.com/idea/download/other.html + Version: "2024.3.1.1", + Amd64: "https://download.jetbrains.com/idea/ideaIU-%s.tar.gz", + Arm64: "https://download.jetbrains.com/idea/ideaIU-%s-aarch64.tar.gz", + }, + enum.IDETypeGoland: { + // list of versions: https://www.jetbrains.com/go/download/other.html + Version: "2024.3.1", + Amd64: "https://download.jetbrains.com/go/goland-%s.tar.gz", + Arm64: "https://download.jetbrains.com/go/goland-%s-aarch64.tar.gz", + }, + enum.IDETypePyCharm: { + // list of versions: https://www.jetbrains.com/pycharm/download/other.html + Version: "2024.3.1.1", + Amd64: "https://download.jetbrains.com/python/pycharm-professional-%s.tar.gz", + Arm64: "https://download.jetbrains.com/python/pycharm-professional-%s-aarch64.tar.gz", + }, + enum.IDETypeWebStorm: { + // list of versions: https://www.jetbrains.com/webstorm/download/other.html + Version: "2024.3.1.1", + Amd64: "https://download.jetbrains.com/webstorm/WebStorm-%s.tar.gz", + Arm64: "https://download.jetbrains.com/webstorm/WebStorm-%s-aarch64.tar.gz", + }, + enum.IDETypeCLion: { + // list of versions: https://www.jetbrains.com/clion/download/other.html + Version: "2024.3.1.1", + Amd64: "https://download.jetbrains.com/cpp/CLion-%s.tar.gz", + Arm64: "https://download.jetbrains.com/cpp/CLion-%s-aarch64.tar.gz", + }, + enum.IDETypePHPStorm: { + // list of versions: https://www.jetbrains.com/phpstorm/download/other.html + Version: "2024.3.1.1", + Amd64: "https://download.jetbrains.com/webide/PhpStorm-%s.tar.gz", + Arm64: "https://download.jetbrains.com/webide/PhpStorm-%s-aarch64.tar.gz", + }, + enum.IDETypeRubyMine: { + // list of versions: https://www.jetbrains.com/ruby/download/other.html + Version: "2024.3.1.1", + Amd64: "https://download.jetbrains.com/ruby/RubyMine-%s.tar.gz", + Arm64: "https://download.jetbrains.com/ruby/RubyMine-%s-aarch64.tar.gz", + }, + enum.IDETypeRider: { + // list of versions: https://www.jetbrains.com/ruby/download/other.html + Version: "2024.3.1.1", + Amd64: "https://download.jetbrains.com/rider/JetBrains.Rider-%s.tar.gz", + Arm64: "https://download.jetbrains.com/rider/JetBrains.Rider-%s-aarch64.tar.gz", + }, +} + +type JetBrainsSpecs struct { + IDEType enum.IDEType + DownloadURls JetBrainsIDEDownloadURLTemplates +}