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
This commit is contained in:
Deepak Bhatt 2025-01-07 13:10:54 +00:00 committed by Harness
parent 2b59a4f68f
commit 19363d2b1a
18 changed files with 387 additions and 123 deletions

View File

@ -130,8 +130,9 @@ func getGitspaceScheme(ideType enum.IDEType, gitspaceSchemeFromMetadata string)
return gitspaceSchemeFromMetadata, nil return gitspaceSchemeFromMetadata, nil
case enum.IDETypeVSCode: case enum.IDETypeVSCode:
return "ssh", nil return "ssh", nil
case enum.IDETypeIntellij: case enum.IDETypeIntelliJ, enum.IDETypePyCharm, enum.IDETypeGoland, enum.IDETypeWebStorm, enum.IDETypeCLion,
return gitspaceSchemeFromMetadata, nil enum.IDETypePHPStorm, enum.IDETypeRubyMine, enum.IDETypeRider:
return "ssh", nil
default: default:
return "", fmt.Errorf("unknown ideType %s", ideType) return "", fmt.Errorf("unknown ideType %s", ideType)
} }

View File

@ -200,9 +200,17 @@ func AddIDECustomizationsArg(
devcontainerConfig types.DevcontainerConfig, devcontainerConfig types.DevcontainerConfig,
args map[gitspaceTypes.IDEArg]interface{}, args map[gitspaceTypes.IDEArg]interface{},
) map[gitspaceTypes.IDEArg]interface{} { ) map[gitspaceTypes.IDEArg]interface{} {
if ideService.Type() == enum.IDETypeVSCodeWeb || ideService.Type() == enum.IDETypeVSCode { switch ideService.Type() {
if devcontainerConfig.Customizations.ExtractVSCodeSpec() != nil { case enum.IDETypeVSCodeWeb, enum.IDETypeVSCode:
args[gitspaceTypes.VSCodeCustomizationArg] = *devcontainerConfig.Customizations.ExtractVSCodeSpec() 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 return args
@ -212,11 +220,18 @@ func AddIDEDownloadURLArg(
ideService ide.IDE, ideService ide.IDE,
args map[gitspaceTypes.IDEArg]interface{}, args map[gitspaceTypes.IDEArg]interface{},
) map[gitspaceTypes.IDEArg]interface{} { ) map[gitspaceTypes.IDEArg]interface{} {
if ideService.Type() == enum.IDETypeIntellij { if !enum.IsJetBrainsIDE(ideService.Type()) {
args[gitspaceTypes.IDEDownloadURLArg] = types.IntellijDownloadURL{ // currently download url is only need for jetbrains IDEs
Arm64: fmt.Sprintf(enum.IDEIntellijDownloadURLArm64Template, enum.IDEIntellijVer), return args
Amd64: fmt.Sprintf(enum.IDEIntellijDownloadURLAmd64Template, enum.IDEIntellijVer), }
}
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 return args
@ -226,10 +241,15 @@ func AddIDEDirNameArg(
ideService ide.IDE, ideService ide.IDE,
args map[gitspaceTypes.IDEArg]interface{}, args map[gitspaceTypes.IDEArg]interface{},
) map[gitspaceTypes.IDEArg]interface{} { ) map[gitspaceTypes.IDEArg]interface{} {
if ideService.Type() == enum.IDETypeIntellij { if !enum.IsJetBrainsIDE(ideService.Type()) {
dirname := path.Join(".cache", "JetBrains", "RemoteDev", "dist", "intellij") // currently dirname is only need for jetbrains IDEs
args[gitspaceTypes.IDEDIRNameArg] = dirname return args
} }
ideType := ideService.Type()
dirname := path.Join(".cache", "JetBrains", "RemoteDev", "dist", ideType.String())
args[gitspaceTypes.IDEDIRNameArg] = dirname
return args return args
} }

View File

@ -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( func (e *EmbeddedDockerOrchestrator) setupGitspaceAndIDE(
ctx context.Context, ctx context.Context,
exec *devcontainer.Exec, exec *devcontainer.Exec,

View File

@ -23,18 +23,18 @@ import (
func getIDEDownloadURL( func getIDEDownloadURL(
args map[gitspaceTypes.IDEArg]interface{}, args map[gitspaceTypes.IDEArg]interface{},
) (types.IntellijDownloadURL, error) { ) (types.IDEDownloadURLs, error) {
downloadURL, exists := args[gitspaceTypes.IDEDownloadURLArg] downloadURL, exists := args[gitspaceTypes.IDEDownloadURLArg]
if !exists { 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 { 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) { func getIDEDirName(args map[gitspaceTypes.IDEArg]interface{}) (string, error) {

View File

@ -24,11 +24,22 @@ type Factory struct {
ides map[enum.IDEType]IDE 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 := make(map[enum.IDEType]IDE)
ides[enum.IDETypeVSCode] = vscode ides[enum.IDETypeVSCode] = vscode
ides[enum.IDETypeVSCodeWeb] = vscodeWeb 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} return Factory{ides: ides}
} }

View File

@ -28,54 +28,58 @@ import (
"github.com/harness/gitness/types/enum" "github.com/harness/gitness/types/enum"
) )
var _ IDE = (*Intellij)(nil) var _ IDE = (*JetBrainsIDE)(nil)
const ( const (
templateSetupIntellij string = "setup_intellij.sh" templateSetupJetBrainsIDE string = "setup_jetbrains_ide.sh"
templateRunRemoteIDEIntellij string = "run_intellij.sh" templateRunRemoteJetBrainsIDE string = "run_jetbrains_ide.sh"
intellijURLScheme string = "jetbrains-gateway" intellijURLScheme string = "jetbrains-gateway"
) )
type IntellijConfig struct { type JetBrainsIDEConfig struct {
Port int Port int
} }
type Intellij struct { type JetBrainsIDE struct {
config IntellijConfig ideType enum.IDEType
config JetBrainsIDEConfig
} }
func NewIntellijService(config *IntellijConfig) *Intellij { func NewJetBrainsIDEService(config *JetBrainsIDEConfig, ideType enum.IDEType) *JetBrainsIDE {
return &Intellij{config: *config} return &JetBrainsIDE{
ideType: ideType,
config: *config,
}
} }
// Setup installs the SSH server inside the container. // Setup installs the SSH server inside the container.
func (ij *Intellij) Setup( func (jb *JetBrainsIDE) Setup(
ctx context.Context, ctx context.Context,
exec *devcontainer.Exec, exec *devcontainer.Exec,
args map[gitspaceTypes.IDEArg]interface{}, args map[gitspaceTypes.IDEArg]interface{},
gitspaceLogger gitspaceTypes.GitspaceLogger, gitspaceLogger gitspaceTypes.GitspaceLogger,
) error { ) error {
gitspaceLogger.Info("Installing ssh-server inside container") gitspaceLogger.Info("Installing ssh-server inside container")
err := ij.setupSSHServer(ctx, exec, gitspaceLogger) err := jb.setupSSHServer(ctx, exec, gitspaceLogger)
if err != nil { if err != nil {
return fmt.Errorf("failed to setup SSH server: %w", err) return fmt.Errorf("failed to setup SSH server: %w", err)
} }
gitspaceLogger.Info("Successfully installed ssh-server") 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...") gitspaceLogger.Info("IDE setup output...")
err = ij.setupIntellijIDE(ctx, exec, args, gitspaceLogger) err = jb.setupIntellijIDE(ctx, exec, args, gitspaceLogger)
if err != nil { 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") gitspaceLogger.Info("Successfully set up IDE inside container")
return nil return nil
} }
func (ij *Intellij) setupSSHServer( func (jb *JetBrainsIDE) setupSSHServer(
ctx context.Context, ctx context.Context,
exec *devcontainer.Exec, exec *devcontainer.Exec,
gitspaceLogger gitspaceTypes.GitspaceLogger, gitspaceLogger gitspaceTypes.GitspaceLogger,
@ -100,7 +104,7 @@ func (ij *Intellij) setupSSHServer(
return nil return nil
} }
func (ij *Intellij) setupIntellijIDE( func (jb *JetBrainsIDE) setupIntellijIDE(
ctx context.Context, ctx context.Context,
exec *devcontainer.Exec, exec *devcontainer.Exec,
args map[gitspaceTypes.IDEArg]interface{}, args map[gitspaceTypes.IDEArg]interface{},
@ -126,11 +130,11 @@ func (ij *Intellij) setupIntellijIDE(
payload.IdeDirName = dirName payload.IdeDirName = dirName
intellijIDEScript, err := utils.GenerateScriptFromTemplate( intellijIDEScript, err := utils.GenerateScriptFromTemplate(
templateSetupIntellij, &payload) templateSetupJetBrainsIDE, &payload)
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
"failed to generate scipt to setup intellij idea from template %s: %w", "failed to generate scipt to setup intellij idea from template %s: %w",
templateSetupIntellij, templateSetupJetBrainsIDE,
err, err,
) )
} }
@ -138,43 +142,43 @@ func (ij *Intellij) setupIntellijIDE(
err = exec.ExecuteCommandInHomeDirAndLog(ctx, intellijIDEScript, err = exec.ExecuteCommandInHomeDirAndLog(ctx, intellijIDEScript,
false, gitspaceLogger, true) false, gitspaceLogger, true)
if err != nil { 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 return nil
} }
// Run runs the SSH server inside the container. // Run runs the SSH server inside the container.
func (ij *Intellij) Run( func (jb *JetBrainsIDE) Run(
ctx context.Context, ctx context.Context,
exec *devcontainer.Exec, exec *devcontainer.Exec,
args map[gitspaceTypes.IDEArg]interface{}, args map[gitspaceTypes.IDEArg]interface{},
gitspaceLogger gitspaceTypes.GitspaceLogger, gitspaceLogger gitspaceTypes.GitspaceLogger,
) error { ) error {
gitspaceLogger.Info("SSH server run output...") gitspaceLogger.Info("SSH server run output...")
err := ij.runSSHServer(ctx, exec, args, gitspaceLogger) err := jb.runSSHServer(ctx, exec, args, gitspaceLogger)
if err != nil { if err != nil {
return err return err
} }
gitspaceLogger.Info("Successfully run ssh-server") gitspaceLogger.Info("Successfully run ssh-server")
gitspaceLogger.Info("Run Remote IntelliJ IDE...") gitspaceLogger.Info("Run Remote IntelliJ IdeType...")
err = ij.runRemoteIDE(ctx, exec, args, gitspaceLogger) err = jb.runRemoteIDE(ctx, exec, args, gitspaceLogger)
if err != nil { if err != nil {
return err return err
} }
gitspaceLogger.Info("Successfully Run Remote IntelliJ IDE") gitspaceLogger.Info("Successfully Run Remote IntelliJ IdeType")
return nil return nil
} }
func (ij *Intellij) runSSHServer( func (jb *JetBrainsIDE) runSSHServer(
ctx context.Context, ctx context.Context,
exec *devcontainer.Exec, exec *devcontainer.Exec,
_ map[gitspaceTypes.IDEArg]interface{}, _ map[gitspaceTypes.IDEArg]interface{},
gitspaceLogger gitspaceTypes.GitspaceLogger, gitspaceLogger gitspaceTypes.GitspaceLogger,
) error { ) error {
payload := gitspaceTypes.RunSSHServerPayload{ payload := gitspaceTypes.RunSSHServerPayload{
Port: strconv.Itoa(ij.config.Port), Port: strconv.Itoa(jb.config.Port),
} }
runSSHScript, err := utils.GenerateScriptFromTemplate( runSSHScript, err := utils.GenerateScriptFromTemplate(
templateRunSSHServer, &payload) templateRunSSHServer, &payload)
@ -192,7 +196,7 @@ func (ij *Intellij) runSSHServer(
return nil return nil
} }
func (ij *Intellij) runRemoteIDE( func (jb *JetBrainsIDE) runRemoteIDE(
ctx context.Context, ctx context.Context,
exec *devcontainer.Exec, exec *devcontainer.Exec,
args map[gitspaceTypes.IDEArg]interface{}, args map[gitspaceTypes.IDEArg]interface{},
@ -216,32 +220,36 @@ func (ij *Intellij) runRemoteIDE(
payload.IdeDirName = dirName payload.IdeDirName = dirName
runSSHScript, err := utils.GenerateScriptFromTemplate( runSSHScript, err := utils.GenerateScriptFromTemplate(
templateRunRemoteIDEIntellij, &payload) templateRunRemoteJetBrainsIDE, &payload)
if err != nil { if err != nil {
return fmt.Errorf( 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) err = exec.ExecuteCommandInHomeDirAndLog(ctx, runSSHScript, false, gitspaceLogger, true)
if err != nil { 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 return nil
} }
// Port returns the port on which the ssh-server is listening. // 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{ return &types.GitspacePort{
Port: ij.config.Port, Port: jb.config.Port,
Protocol: enum.CommunicationProtocolSSH, 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). // 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) 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{ ideURL := url.URL{
Scheme: intellijURLScheme, Scheme: intellijURLScheme,
Host: "", // Empty since we include the host and port in the path 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() return ideURL.String()
} }
func (ij *Intellij) Type() enum.IDEType {
return enum.IDETypeIntellij
}

View File

@ -15,13 +15,15 @@
package ide package ide
import ( import (
"github.com/harness/gitness/types/enum"
"github.com/google/wire" "github.com/google/wire"
) )
var WireSet = wire.NewSet( var WireSet = wire.NewSet(
ProvideVSCodeWebService, ProvideVSCodeWebService,
ProvideVSCodeService, ProvideVSCodeService,
ProvideIntellijService, ProvideJetBrainsIDEsService,
ProvideIDEFactory, ProvideIDEFactory,
) )
@ -33,14 +35,23 @@ func ProvideVSCodeService(config *VSCodeConfig) *VSCode {
return NewVsCodeService(config) return NewVsCodeService(config)
} }
func ProvideIntellijService(config *IntellijConfig) *Intellij { func ProvideJetBrainsIDEsService(config *JetBrainsIDEConfig) map[enum.IDEType]*JetBrainsIDE {
return NewIntellijService(config) 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( func ProvideIDEFactory(
vscode *VSCode, vscode *VSCode,
vscodeWeb *VSCodeWeb, vscodeWeb *VSCodeWeb,
intellij *Intellij, jetBrainsIDEsMap map[enum.IDEType]*JetBrainsIDE,
) Factory { ) Factory {
return NewFactory(vscode, vscodeWeb, intellij) return NewFactory(vscode, vscodeWeb, jetBrainsIDEsMap)
} }

View File

@ -42,8 +42,9 @@ func InstallTools(
return err return err
} }
return nil return nil
case enum.IDETypeIntellij: case enum.IDETypeIntelliJ, enum.IDETypePyCharm, enum.IDETypeGoland, enum.IDETypeWebStorm, enum.IDETypeCLion,
err := InstallToolsForIntellij(ctx, exec, gitspaceLogger) enum.IDETypePHPStorm, enum.IDETypeRubyMine, enum.IDETypeRider:
err := InstallToolsForJetBrains(ctx, exec, ideType, gitspaceLogger)
if err != nil { if err != nil {
return err return err
} }
@ -101,9 +102,10 @@ func InstallToolsForVsCode(
return nil return nil
} }
func InstallToolsForIntellij( func InstallToolsForJetBrains(
ctx context.Context, ctx context.Context,
exec *devcontainer.Exec, exec *devcontainer.Exec,
ideType enum.IDEType,
gitspaceLogger types.GitspaceLogger, gitspaceLogger types.GitspaceLogger,
) error { ) error {
script, err := GenerateScriptFromTemplate( script, err := GenerateScriptFromTemplate(
@ -112,15 +114,15 @@ func InstallToolsForIntellij(
}) })
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
"failed to generate scipt to install tools for intellij from template %s: %w", "failed to generate scipt to install tools for %s from template %s: %w",
templateIntellijToolsInstallation, err) 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) err = exec.ExecuteCommandInHomeDirAndLog(ctx, script, true, gitspaceLogger, false)
if err != nil { 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 return nil
} }

View File

@ -23,11 +23,12 @@ import (
type IDEArg string type IDEArg string
const ( const (
VSCodeCustomizationArg IDEArg = "VSCODE_CUSTOMIZATION" VSCodeCustomizationArg IDEArg = "VSCODE_CUSTOMIZATION"
VSCodeProxyURIArg IDEArg = "VSCODE_PROXY_URI" JetBrainsCustomizationArg IDEArg = "JETBRAINS_CUSTOMIZATION"
IDERepoNameArg IDEArg = "IDE_REPO_NAME" VSCodeProxyURIArg IDEArg = "VSCODE_PROXY_URI"
IDEDownloadURLArg IDEArg = "IDE_DOWNLOAD_URL" IDERepoNameArg IDEArg = "IDE_REPO_NAME"
IDEDIRNameArg IDEArg = "IDE_DIR_NAME" IDEDownloadURLArg IDEArg = "IDE_DOWNLOAD_URL"
IDEDIRNameArg IDEArg = "IDE_DIR_NAME"
) )
type GitspaceLogger interface { type GitspaceLogger interface {

View File

@ -442,9 +442,9 @@ func ProvideIDEVSCodeConfig(config *types.Config) *ide.VSCodeConfig {
} }
} }
// ProvideIDEIntellijConfig loads the Intellij IDE config from the main config. // ProvideIDEIntellijConfig loads the IdeType IDE config from the main config.
func ProvideIDEIntellijConfig(config *types.Config) *ide.IntellijConfig { func ProvideIDEIntellijConfig(config *types.Config) *ide.JetBrainsIDEConfig {
return &ide.IntellijConfig{ return &ide.JetBrainsIDEConfig{
Port: config.IDE.Intellij.Port, Port: config.IDE.Intellij.Port,
} }
} }

View File

@ -333,9 +333,9 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
vsCode := ide.ProvideVSCodeService(vsCodeConfig) vsCode := ide.ProvideVSCodeService(vsCodeConfig)
vsCodeWebConfig := server.ProvideIDEVSCodeWebConfig(config) vsCodeWebConfig := server.ProvideIDEVSCodeWebConfig(config)
vsCodeWeb := ide.ProvideVSCodeWebService(vsCodeWebConfig) vsCodeWeb := ide.ProvideVSCodeWebService(vsCodeWebConfig)
intellijConfig := server.ProvideIDEIntellijConfig(config) jetBrainsIDEConfig := server.ProvideIDEIntellijConfig(config)
intellij := ide.ProvideIntellijService(intellijConfig) v := ide.ProvideJetBrainsIDEsService(jetBrainsIDEConfig)
ideFactory := ide.ProvideIDEFactory(vsCode, vsCodeWeb, intellij) ideFactory := ide.ProvideIDEFactory(vsCode, vsCodeWeb, v)
passwordResolver := secret.ProvidePasswordResolver() passwordResolver := secret.ProvidePasswordResolver()
resolverFactory := secret.ProvideResolverFactory(passwordResolver) resolverFactory := secret.ProvideResolverFactory(passwordResolver)
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, platformConnector, infraProvisioner, containerOrchestrator, eventsReporter, orchestratorConfig, ideFactory, resolverFactory) 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) serviceaccountController := serviceaccount.NewController(principalUID, authorizer, principalStore, spaceStore, repoStore, tokenStore)
principalController := principal.ProvideController(principalStore, authorizer) principalController := principal.ProvideController(principalStore, authorizer)
usergroupController := usergroup2.ProvideController(userGroupStore, spaceStore, authorizer, searchService) usergroupController := usergroup2.ProvideController(userGroupStore, spaceStore, authorizer, searchService)
v := check2.ProvideCheckSanitizers() v2 := check2.ProvideCheckSanitizers()
checkController := check2.ProvideController(transactor, authorizer, spaceStore, checkStore, spaceCache, repoFinder, gitInterface, v, streamer) checkController := check2.ProvideController(transactor, authorizer, spaceStore, checkStore, spaceCache, repoFinder, gitInterface, v2, streamer)
systemController := system.NewController(principalStore, config) systemController := system.NewController(principalStore, config)
blobConfig, err := server.ProvideBlobStoreConfig(config) blobConfig, err := server.ProvideBlobStoreConfig(config)
if err != nil { if err != nil {

View File

@ -17,12 +17,15 @@ package types
import ( import (
"encoding/json" "encoding/json"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
const ( const (
GitspaceCustomizationsKey CustomizationsKey = "harnessGitspaces" GitspaceCustomizationsKey CustomizationsKey = "harnessGitspaces"
VSCodeCustomizationsKey CustomizationsKey = "vscode" VSCodeCustomizationsKey CustomizationsKey = "vscode"
JetBrainsCustomizationsKey CustomizationsKey = "jetbrains"
) )
type CustomizationsKey string type CustomizationsKey string
@ -31,8 +34,116 @@ func (ck CustomizationsKey) String() string {
return string(ck) 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{} 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 { func (dcc DevContainerConfigCustomizations) ExtractGitspaceSpec() *GitspaceCustomizationSpecs {
val, ok := dcc[GitspaceCustomizationsKey.String()] val, ok := dcc[GitspaceCustomizationsKey.String()]
if !ok { if !ok {
@ -84,14 +195,28 @@ func (dcc DevContainerConfigCustomizations) ExtractVSCodeSpec() *VSCodeCustomiza
return &vsCodeCustomizationSpecs return &vsCodeCustomizationSpecs
} }
type VSCodeCustomizationSpecs struct { func (dcc DevContainerConfigCustomizations) ExtractJetBrainsSpecs() *JetBrainsCustomizationSpecs {
Extensions []string `json:"extensions"` data, ok := dcc[JetBrainsCustomizationsKey.String()]
Settings map[string]interface{} `json:"settings"` 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 { rawData, err := json.Marshal(data)
Connectors []struct { if err != nil {
Type string `json:"type"` // Log the error during marshalling and return nil
ID string `json:"identifier"` log.Printf("Failed to marshal data for key %q: %v", JetBrainsCustomizationsKey, err)
} `json:"connectors"` 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
} }

View File

@ -16,12 +16,39 @@ package enum
type IDEType string 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 ( const (
IDETypeVSCode IDEType = "vs_code" IDETypeVSCode IDEType = "vs_code"
IDETypeVSCodeWeb IDEType = "vs_code_web" 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
}

View File

@ -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"
)

View File

@ -14,7 +14,9 @@
package types package types
type IntellijDownloadURL struct { type IDEDownloadURLs struct {
Arm64 string Arm64Sha string
Amd64 string Amd64Sha string
Arm64 string
Amd64 string
} }

81
types/jetbrains.go Normal file
View File

@ -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
}