|
5 | 5 | package main
|
6 | 6 |
|
7 | 7 | import (
|
| 8 | + "archive/zip" |
8 | 9 | "bytes"
|
9 | 10 | "context"
|
10 | 11 | "encoding/json"
|
11 | 12 | "errors"
|
12 | 13 | "fmt"
|
| 14 | + "io" |
| 15 | + "io/fs" |
13 | 16 | "io/ioutil"
|
14 | 17 | "net"
|
15 | 18 | "net/http"
|
@@ -420,12 +423,16 @@ func launch(launchCtx *LaunchContext) {
|
420 | 423 | launchCtx.projectConfigDir = fmt.Sprintf("%s/RemoteDev-%s/%s", launchCtx.configDir, launchCtx.info.ProductCode, strings.ReplaceAll(launchCtx.projectContextDir, "/", "_"))
|
421 | 424 | launchCtx.env = resolveLaunchContextEnv(launchCtx.configDir, launchCtx.systemDir)
|
422 | 425 |
|
423 |
| - // sync initial options |
424 |
| - err = syncOptions(launchCtx) |
| 426 | + err = syncInitialContent(launchCtx, Options) |
425 | 427 | if err != nil {
|
426 | 428 | log.WithError(err).Error("failed to sync initial options")
|
427 | 429 | }
|
428 | 430 |
|
| 431 | + err = syncInitialContent(launchCtx, Plugins) |
| 432 | + if err != nil { |
| 433 | + log.WithError(err).Error("failed to sync initial plugins") |
| 434 | + } |
| 435 | + |
429 | 436 | // install project plugins
|
430 | 437 | version_2022_1, _ := version.NewVersion("2022.1")
|
431 | 438 | if version_2022_1.LessThanOrEqual(launchCtx.backendVersion) {
|
@@ -710,53 +717,181 @@ func resolveProductInfo(backendDir string) (*ProductInfo, error) {
|
710 | 717 | return &info, err
|
711 | 718 | }
|
712 | 719 |
|
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) |
715 | 784 | if err != nil {
|
716 | 785 | return err
|
717 | 786 | }
|
| 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 | + } |
718 | 819 | var srcDirs []string
|
719 | 820 | 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), |
724 | 825 | } {
|
725 | 826 | srcStat, err := os.Stat(srcDir)
|
726 | 827 | if os.IsNotExist(err) {
|
727 | 828 | // nothing to sync
|
728 | 829 | continue
|
729 | 830 | }
|
730 | 831 | if err != nil {
|
731 |
| - return err |
| 832 | + return nil, err |
732 | 833 | }
|
733 | 834 | if !srcStat.IsDir() {
|
734 |
| - return fmt.Errorf("%s is not a directory", srcDir) |
| 835 | + return nil, fmt.Errorf("%s is not a directory", srcDir) |
735 | 836 | }
|
736 | 837 | srcDirs = append(srcDirs, srcDir)
|
737 | 838 | }
|
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 |
741 | 846 | }
|
| 847 | + defer r.Close() |
742 | 848 |
|
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") |
748 | 851 | }
|
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) |
750 | 859 | if err != nil {
|
751 | 860 | return err
|
752 | 861 | }
|
| 862 | + defer r.Close() |
753 | 863 |
|
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() |
757 | 866 | if err != nil {
|
758 | 867 | return err
|
759 | 868 | }
|
| 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 | + } |
760 | 895 | }
|
761 | 896 | return nil
|
762 | 897 | }
|
|
0 commit comments