Skip to content

Commit 5303260

Browse files
authored
fix EXP-626: support jb plugins from image (#18732)
1 parent 6552fd5 commit 5303260

File tree

2 files changed

+181
-22
lines changed

2 files changed

+181
-22
lines changed

components/ide/jetbrains/launcher/main.go

Lines changed: 157 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
package main
66

77
import (
8+
"archive/zip"
89
"bytes"
910
"context"
1011
"encoding/json"
1112
"errors"
1213
"fmt"
14+
"io"
15+
"io/fs"
1316
"io/ioutil"
1417
"net"
1518
"net/http"
@@ -420,12 +423,16 @@ func launch(launchCtx *LaunchContext) {
420423
launchCtx.projectConfigDir = fmt.Sprintf("%s/RemoteDev-%s/%s", launchCtx.configDir, launchCtx.info.ProductCode, strings.ReplaceAll(launchCtx.projectContextDir, "/", "_"))
421424
launchCtx.env = resolveLaunchContextEnv(launchCtx.configDir, launchCtx.systemDir)
422425

423-
// sync initial options
424-
err = syncOptions(launchCtx)
426+
err = syncInitialContent(launchCtx, Options)
425427
if err != nil {
426428
log.WithError(err).Error("failed to sync initial options")
427429
}
428430

431+
err = syncInitialContent(launchCtx, Plugins)
432+
if err != nil {
433+
log.WithError(err).Error("failed to sync initial plugins")
434+
}
435+
429436
// install project plugins
430437
version_2022_1, _ := version.NewVersion("2022.1")
431438
if version_2022_1.LessThanOrEqual(launchCtx.backendVersion) {
@@ -710,53 +717,181 @@ func resolveProductInfo(backendDir string) (*ProductInfo, error) {
710717
return &info, err
711718
}
712719

713-
func syncOptions(launchCtx *LaunchContext) error {
714-
userHomeDir, err := os.UserHomeDir()
720+
type SyncTarget string
721+
722+
const (
723+
Options SyncTarget = "options"
724+
Plugins SyncTarget = "plugins"
725+
)
726+
727+
func syncInitialContent(launchCtx *LaunchContext, target SyncTarget) error {
728+
destDir, err, alreadySynced := ensureInitialSyncDest(launchCtx, target)
729+
if alreadySynced {
730+
log.Infof("initial %s is already synced, skipping", target)
731+
return nil
732+
}
733+
if err != nil {
734+
return err
735+
}
736+
737+
srcDirs, err := collectSyncSources(launchCtx, target)
738+
if err != nil {
739+
return err
740+
}
741+
if len(srcDirs) == 0 {
742+
// nothing to sync
743+
return nil
744+
}
745+
746+
for _, srcDir := range srcDirs {
747+
if target == Plugins {
748+
files, err := ioutil.ReadDir(srcDir)
749+
if err != nil {
750+
return err
751+
}
752+
753+
for _, file := range files {
754+
err := syncPlugin(file, srcDir, destDir)
755+
if err != nil {
756+
log.WithError(err).WithField("file", file.Name()).WithField("srcDir", srcDir).WithField("destDir", destDir).Error("failed to sync plugin")
757+
}
758+
}
759+
} else {
760+
cp := exec.Command("cp", "-rf", srcDir+"/.", destDir)
761+
err = cp.Run()
762+
if err != nil {
763+
return err
764+
}
765+
}
766+
}
767+
return nil
768+
}
769+
770+
func syncPlugin(file fs.FileInfo, srcDir, destDir string) error {
771+
if file.IsDir() {
772+
_, err := os.Stat(filepath.Join(destDir, file.Name()))
773+
if !os.IsNotExist(err) {
774+
log.WithField("plugin", file.Name()).Info("plugin is already synced, skipping")
775+
return nil
776+
}
777+
return exec.Command("cp", "-rf", filepath.Join(srcDir, file.Name()), destDir).Run()
778+
}
779+
if filepath.Ext(file.Name()) != ".zip" {
780+
return nil
781+
}
782+
archiveFile := filepath.Join(srcDir, file.Name())
783+
rootDir, err := getRootDirFromArchive(archiveFile)
715784
if err != nil {
716785
return err
717786
}
787+
_, err = os.Stat(filepath.Join(destDir, rootDir))
788+
if !os.IsNotExist(err) {
789+
log.WithField("plugin", rootDir).Info("plugin is already synced, skipping")
790+
return nil
791+
}
792+
return unzipArchive(archiveFile, destDir)
793+
}
794+
795+
func ensureInitialSyncDest(launchCtx *LaunchContext, target SyncTarget) (string, error, bool) {
796+
targetDestDir := launchCtx.projectConfigDir
797+
if target == Plugins {
798+
targetDestDir = launchCtx.backendDir
799+
}
800+
destDir := fmt.Sprintf("%s/%s", targetDestDir, target)
801+
if target == Options {
802+
_, err := os.Stat(destDir)
803+
if !os.IsNotExist(err) {
804+
return "", nil, true
805+
}
806+
err = os.MkdirAll(destDir, os.ModePerm)
807+
if err != nil {
808+
return "", err, false
809+
}
810+
}
811+
return destDir, nil, false
812+
}
813+
814+
func collectSyncSources(launchCtx *LaunchContext, target SyncTarget) ([]string, error) {
815+
userHomeDir, err := os.UserHomeDir()
816+
if err != nil {
817+
return nil, err
818+
}
718819
var srcDirs []string
719820
for _, srcDir := range []string{
720-
fmt.Sprintf("%s/.gitpod/jetbrains/options", userHomeDir),
721-
fmt.Sprintf("%s/.gitpod/jetbrains/%s/options", userHomeDir, launchCtx.alias),
722-
fmt.Sprintf("%s/.gitpod/jetbrains/options", launchCtx.projectDir),
723-
fmt.Sprintf("%s/.gitpod/jetbrains/%s/options", launchCtx.projectDir, launchCtx.alias),
821+
fmt.Sprintf("%s/.gitpod/jetbrains/%s", userHomeDir, target),
822+
fmt.Sprintf("%s/.gitpod/jetbrains/%s/%s", userHomeDir, launchCtx.alias, target),
823+
fmt.Sprintf("%s/.gitpod/jetbrains/%s", launchCtx.projectDir, target),
824+
fmt.Sprintf("%s/.gitpod/jetbrains/%s/%s", launchCtx.projectDir, launchCtx.alias, target),
724825
} {
725826
srcStat, err := os.Stat(srcDir)
726827
if os.IsNotExist(err) {
727828
// nothing to sync
728829
continue
729830
}
730831
if err != nil {
731-
return err
832+
return nil, err
732833
}
733834
if !srcStat.IsDir() {
734-
return fmt.Errorf("%s is not a directory", srcDir)
835+
return nil, fmt.Errorf("%s is not a directory", srcDir)
735836
}
736837
srcDirs = append(srcDirs, srcDir)
737838
}
738-
if len(srcDirs) == 0 {
739-
// nothing to sync
740-
return nil
839+
return srcDirs, nil
840+
}
841+
842+
func getRootDirFromArchive(zipPath string) (string, error) {
843+
r, err := zip.OpenReader(zipPath)
844+
if err != nil {
845+
return "", err
741846
}
847+
defer r.Close()
742848

743-
destDir := fmt.Sprintf("%s/options", launchCtx.projectConfigDir)
744-
_, err = os.Stat(destDir)
745-
if !os.IsNotExist(err) {
746-
// already synced skipping, i.e. restart of jb backend
747-
return nil
849+
if len(r.File) == 0 {
850+
return "", fmt.Errorf("empty archive")
748851
}
749-
err = os.MkdirAll(destDir, os.ModePerm)
852+
853+
// Assuming the first file in the zip is the root directory or a file in the root directory
854+
return strings.SplitN(r.File[0].Name, "/", 2)[0], nil
855+
}
856+
857+
func unzipArchive(src, dest string) error {
858+
r, err := zip.OpenReader(src)
750859
if err != nil {
751860
return err
752861
}
862+
defer r.Close()
753863

754-
for _, srcDir := range srcDirs {
755-
cp := exec.Command("cp", "-rf", srcDir+"/.", destDir)
756-
err = cp.Run()
864+
for _, f := range r.File {
865+
rc, err := f.Open()
757866
if err != nil {
758867
return err
759868
}
869+
defer rc.Close()
870+
871+
fpath := filepath.Join(dest, f.Name)
872+
if f.FileInfo().IsDir() {
873+
err := os.MkdirAll(fpath, os.ModePerm)
874+
if err != nil {
875+
return err
876+
}
877+
} else {
878+
fdir := filepath.Dir(fpath)
879+
err := os.MkdirAll(fdir, os.ModePerm)
880+
if err != nil {
881+
return err
882+
}
883+
884+
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
885+
if err != nil {
886+
return err
887+
}
888+
defer outFile.Close()
889+
890+
_, err = io.Copy(outFile, rc)
891+
if err != nil {
892+
return err
893+
}
894+
}
760895
}
761896
return nil
762897
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
# Copyright (c) 2023 Gitpod GmbH. All rights reserved.
3+
# Licensed under the GNU Affero General Public License (AGPL).
4+
# See License.AGPL.txt in the project root for license information.
5+
6+
set -Eeuo pipefail
7+
8+
DIR="$(dirname "$(realpath "$0")")"
9+
COMPONENT="$(basename "$DIR")"
10+
cd "$DIR"
11+
12+
# build
13+
go build .
14+
echo "$COMPONENT built"
15+
16+
DIST_COMPONENT="jb-launcher"
17+
mv "$COMPONENT" "$DIST_COMPONENT"
18+
echo "rename $COMPONENT to $DIST_COMPONENT"
19+
COMPONENT="$DIST_COMPONENT"
20+
21+
DEST="/ide-desktop"
22+
sudo rm -rf "$DEST/$COMPONENT" && true
23+
sudo mv ./"$COMPONENT" "$DEST"
24+
echo "$COMPONENT in $DEST replaced"

0 commit comments

Comments
 (0)