mirror of https://github.com/harness/drone.git
feat: [CDE-525]: Add support for customization IDE extensions from devcontainer.json (#3057)
* feat: [CDE-525]: lint fixes * feat: [CDE-525]: lint fixes * feat: [CDE-525]: setup vscode web plugins * feat: [CDE-525]: remove unused fields during serde from devcontainer.json * feat: [CDE-525]: remove unused fields during serde from devcontainer.jsonpull/3597/head
parent
323511819d
commit
843dc4df5f
|
@ -156,25 +156,11 @@ func SetupIDE(
|
|||
return nil
|
||||
}
|
||||
|
||||
func RunIDE(
|
||||
ctx context.Context,
|
||||
exec *devcontainer.Exec,
|
||||
ideService ide.IDE,
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger,
|
||||
) error {
|
||||
gitspaceLogger.Info("Running the IDE inside container: " + string(ideService.Type()))
|
||||
err := ideService.Run(ctx, exec, nil, gitspaceLogger)
|
||||
if err != nil {
|
||||
return logStreamWrapError(gitspaceLogger, "Error while running IDE inside container", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunIDEWithArgs(
|
||||
ctx context.Context,
|
||||
exec *devcontainer.Exec,
|
||||
ideService ide.IDE,
|
||||
args map[string]string,
|
||||
args map[string]interface{},
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger,
|
||||
) error {
|
||||
gitspaceLogger.Info("Running the IDE inside container: " + string(ideService.Type()))
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
gitspaceTypes "github.com/harness/gitness/app/gitspace/types"
|
||||
"github.com/harness/gitness/infraprovider"
|
||||
"github.com/harness/gitness/types"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/client"
|
||||
|
@ -208,7 +209,8 @@ func (e *EmbeddedDockerOrchestrator) startStoppedGitspace(
|
|||
}
|
||||
|
||||
// Run IDE setup
|
||||
if err := RunIDE(ctx, exec, ideService, logStreamInstance); err != nil {
|
||||
args := ExtractIDEArgs(ideService, resolvedRepoDetails.DevcontainerConfig)
|
||||
if err := RunIDEWithArgs(ctx, exec, ideService, args, logStreamInstance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -528,7 +530,8 @@ func (e *EmbeddedDockerOrchestrator) buildSetupSteps(
|
|||
exec *devcontainer.Exec,
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger,
|
||||
) error {
|
||||
return RunIDE(ctx, exec, ideService, gitspaceLogger)
|
||||
args := ExtractIDEArgs(ideService, devcontainerConfig)
|
||||
return RunIDEWithArgs(ctx, exec, ideService, args, gitspaceLogger)
|
||||
},
|
||||
StopOnFailure: true,
|
||||
},
|
||||
|
@ -596,6 +599,16 @@ func (e *EmbeddedDockerOrchestrator) buildSetupSteps(
|
|||
}
|
||||
}
|
||||
|
||||
func ExtractIDEArgs(ideService ide.IDE, devcontainerConfig types.DevcontainerConfig) map[string]interface{} {
|
||||
var args = make(map[string]interface{})
|
||||
if ideService.Type() == enum.IDETypeVSCodeWeb || ideService.Type() == enum.IDETypeVSCode {
|
||||
if devcontainerConfig.Customizations.ExtractVSCodeSpec() != nil {
|
||||
args["customization"] = *devcontainerConfig.Customizations.ExtractVSCodeSpec()
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// setupGitspaceAndIDE initializes Gitspace and IDE by registering and executing the setup steps.
|
||||
func (e *EmbeddedDockerOrchestrator) setupGitspaceAndIDE(
|
||||
ctx context.Context,
|
||||
|
|
|
@ -26,13 +26,17 @@ import (
|
|||
type IDE interface {
|
||||
// Setup is responsible for doing all the operations for setting up the IDE in the container e.g. installation,
|
||||
// copying settings and configurations.
|
||||
Setup(ctx context.Context, exec *devcontainer.Exec, gitspaceLogger gitspaceTypes.GitspaceLogger) error
|
||||
Setup(
|
||||
ctx context.Context,
|
||||
exec *devcontainer.Exec,
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger,
|
||||
) error
|
||||
|
||||
// Run runs the IDE and supporting services.
|
||||
Run(
|
||||
ctx context.Context,
|
||||
exec *devcontainer.Exec,
|
||||
args map[string]string,
|
||||
args map[string]interface{},
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger,
|
||||
) error
|
||||
|
||||
|
|
|
@ -77,17 +77,28 @@ func (v *VSCode) Setup(
|
|||
func (v *VSCode) Run(
|
||||
ctx context.Context,
|
||||
exec *devcontainer.Exec,
|
||||
_ map[string]string,
|
||||
args map[string]interface{},
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger,
|
||||
) error {
|
||||
payload := template.RunSSHServerPayload{
|
||||
Port: strconv.Itoa(v.config.Port),
|
||||
}
|
||||
runSSHScript, err := template.GenerateScriptFromTemplate(
|
||||
templateRunSSHServer, &template.RunSSHServerPayload{
|
||||
Port: strconv.Itoa(v.config.Port),
|
||||
})
|
||||
templateRunSSHServer, &payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to generate scipt to run ssh server from template %s: %w", templateRunSSHServer, err)
|
||||
}
|
||||
if args != nil {
|
||||
if customization, exists := args["customization"]; exists {
|
||||
// Perform a type assertion to ensure customization is a VSCodeCustomizationSpecs
|
||||
if vsCodeCustomizationSpecs, ok := customization.(types.VSCodeCustomizationSpecs); ok {
|
||||
gitspaceLogger.Info(fmt.Sprintf("VSCode Customizations %v", vsCodeCustomizationSpecs))
|
||||
} else {
|
||||
return fmt.Errorf("customization is not of type VSCodeCustomizationSpecs")
|
||||
}
|
||||
}
|
||||
}
|
||||
gitspaceLogger.Info("SSH server run output...")
|
||||
err = common.ExecuteCommandInHomeDirAndLog(ctx, exec, runSSHScript, true, gitspaceLogger)
|
||||
if err != nil {
|
||||
|
|
|
@ -36,9 +36,6 @@ import (
|
|||
|
||||
var _ IDE = (*VSCodeWeb)(nil)
|
||||
|
||||
//go:embed script/install_vscode_web.sh
|
||||
var installScript string
|
||||
|
||||
//go:embed script/find_vscode_web_path.sh
|
||||
var findPathScript string
|
||||
|
||||
|
@ -46,6 +43,7 @@ var findPathScript string
|
|||
var mediaFiles embed.FS
|
||||
|
||||
const templateRunVSCodeWeb = "run_vscode_web.sh"
|
||||
const templateSetupVSCodeWeb = "install_vscode_web.sh"
|
||||
const startMarker = "START_MARKER"
|
||||
const endMarker = "END_MARKER"
|
||||
|
||||
|
@ -69,8 +67,17 @@ func (v *VSCodeWeb) Setup(
|
|||
) error {
|
||||
gitspaceLogger.Info("Installing VSCode Web inside container.")
|
||||
gitspaceLogger.Info("IDE setup output...")
|
||||
payload := &template.SetupVSCodeWebPayload{}
|
||||
setupScript, err := template.GenerateScriptFromTemplate(templateSetupVSCodeWeb, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"failed to generate script to setup VSCode Web from template %s: %w",
|
||||
templateRunVSCodeWeb,
|
||||
err,
|
||||
)
|
||||
}
|
||||
outputCh := make(chan []byte)
|
||||
err := exec.ExecuteCommandInHomeDirectory(ctx, installScript, true, false, outputCh)
|
||||
err = exec.ExecuteCommandInHomeDirectory(ctx, setupScript, true, false, outputCh)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to install VSCode Web: %w", err)
|
||||
}
|
||||
|
@ -112,15 +119,17 @@ func (v *VSCodeWeb) Setup(
|
|||
func (v *VSCodeWeb) Run(
|
||||
ctx context.Context,
|
||||
exec *devcontainer.Exec,
|
||||
args map[string]string,
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger) error {
|
||||
args map[string]interface{},
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger,
|
||||
) error {
|
||||
payload := &template.RunVSCodeWebPayload{
|
||||
Port: strconv.Itoa(v.config.Port),
|
||||
}
|
||||
|
||||
if args != nil {
|
||||
// Set ProxyURI only if present in the map
|
||||
if proxyURI, ok := args["VSCODE_PROXY_URI"]; ok {
|
||||
payload.ProxyURI = proxyURI
|
||||
err := updatePayloadFromArgs(args, payload, gitspaceLogger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
runScript, err := template.GenerateScriptFromTemplate(templateRunVSCodeWeb, payload)
|
||||
|
@ -133,12 +142,38 @@ func (v *VSCodeWeb) Run(
|
|||
}
|
||||
gitspaceLogger.Info("Starting IDE ...")
|
||||
outputCh := make(chan []byte)
|
||||
|
||||
// Execute the script in the home directory
|
||||
err = exec.ExecuteCommandInHomeDirectory(ctx, runScript, false, false, outputCh)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run VSCode Web: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updatePayloadFromArgs(
|
||||
args map[string]interface{},
|
||||
payload *template.RunVSCodeWebPayload,
|
||||
gitspaceLogger gitspaceTypes.GitspaceLogger,
|
||||
) error {
|
||||
if proxyURI, exists := args["VSCODE_PROXY_URI"]; exists {
|
||||
// Perform a type assertion to ensure proxyURI is a string
|
||||
proxyURIStr, ok := proxyURI.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("VSCODE_PROXY_URI is not a string")
|
||||
}
|
||||
payload.ProxyURI = proxyURIStr
|
||||
}
|
||||
|
||||
if customization, exists := args["customization"]; exists {
|
||||
// Perform a type assertion to ensure customization is a VSCodeCustomizationSpecs
|
||||
vsCodeCustomizationSpecs, ok := customization.(types.VSCodeCustomizationSpecs)
|
||||
if !ok {
|
||||
return fmt.Errorf("customization is not of type VSCodeCustomizationSpecs")
|
||||
}
|
||||
payload.Extensions = vsCodeCustomizationSpecs.Extensions
|
||||
gitspaceLogger.Info(fmt.Sprintf("VSCode Customizations %v", vsCodeCustomizationSpecs))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -53,9 +53,13 @@ type SetupGitCredentialsPayload struct {
|
|||
}
|
||||
|
||||
type RunVSCodeWebPayload struct {
|
||||
Port string
|
||||
Arguments string
|
||||
ProxyURI string
|
||||
Port string
|
||||
Arguments string
|
||||
ProxyURI string
|
||||
Extensions []string
|
||||
}
|
||||
|
||||
type SetupVSCodeWebPayload struct {
|
||||
}
|
||||
|
||||
type SetupUserPayload struct {
|
||||
|
|
|
@ -5,6 +5,7 @@ echo "Running VSCode Web"
|
|||
# Default port comes from the Go templating variable {{ .Port }}
|
||||
port={{ .Port }}
|
||||
proxyuri="{{ .ProxyURI }}"
|
||||
extensions={{ range .Extensions }}"{{ . }}" {{ end }}
|
||||
|
||||
# Ensure the configuration directory exists
|
||||
mkdir -p $HOME/.config/code-server
|
||||
|
@ -16,6 +17,14 @@ auth: none
|
|||
cert: false
|
||||
EOF
|
||||
|
||||
# Install extensions using code-server CLI and display errors if any
|
||||
for extension in $extensions; do
|
||||
echo "Installing extension: $extension"
|
||||
if ! code-server --install-extension "$extension"; then
|
||||
echo "Error installing extension: $extension" >&2
|
||||
fi
|
||||
done
|
||||
|
||||
# Export the Proxy URI only if set
|
||||
if [ -n "$proxyuri" ]; then
|
||||
export VSCODE_PROXY_URI="$proxyuri"
|
||||
|
|
|
@ -14,10 +14,15 @@
|
|||
|
||||
package types
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
GitspaceCustomizationsKey CustomizationsKey = "harnessGitspaces"
|
||||
VSCodeCustomizationsKey CustomizationsKey = "vscode"
|
||||
)
|
||||
|
||||
type CustomizationsKey string
|
||||
|
@ -45,6 +50,45 @@ func (dcc DevContainerConfigCustomizations) ExtractGitspaceSpec() *GitspaceCusto
|
|||
return &gitspaceSpecs
|
||||
}
|
||||
|
||||
func (dcc DevContainerConfigCustomizations) ExtractVSCodeSpec() *VSCodeCustomizationSpecs {
|
||||
val, ok := dcc[VSCodeCustomizationsKey.String()]
|
||||
if !ok {
|
||||
// Log that the key is missing, but return nil
|
||||
log.Warn().Msgf("VSCode customization key %q not found, returning empty struct",
|
||||
VSCodeCustomizationsKey.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
data, ok := val.(map[string]interface{})
|
||||
if !ok {
|
||||
// Log the type mismatch and return nil
|
||||
log.Warn().Msgf("Unexpected data type for key %q, expected map[string]interface{}, but got %T",
|
||||
VSCodeCustomizationsKey.String(), val)
|
||||
return nil
|
||||
}
|
||||
|
||||
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", VSCodeCustomizationsKey.String(), err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var vsCodeCustomizationSpecs VSCodeCustomizationSpecs
|
||||
if err := json.Unmarshal(rawData, &vsCodeCustomizationSpecs); err != nil {
|
||||
// Log the error during unmarshalling and return nil
|
||||
log.Printf("Failed to unmarshal data for key %q: %v", VSCodeCustomizationsKey.String(), err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &vsCodeCustomizationSpecs
|
||||
}
|
||||
|
||||
type VSCodeCustomizationSpecs struct {
|
||||
Extensions []string `json:"extensions"`
|
||||
Settings map[string]interface{} `json:"settings"`
|
||||
}
|
||||
|
||||
type GitspaceCustomizationSpecs struct {
|
||||
Connectors []struct {
|
||||
Type string `json:"type"`
|
||||
|
|
Loading…
Reference in New Issue