Commit 0d1ca202 authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

do not use system java + fix climax basex bug

parent efd5c2b1
Loading
Loading
Loading
Loading
+99 −35
Original line number Diff line number Diff line
@@ -1158,15 +1158,30 @@ func isInteractiveTTY(f *os.File) bool {
}

func ensureJava() (string, error) {
	if javaBin, err := exec.LookPath("java"); err == nil {
		return javaBin, nil
	}

	curSys := currentSystem()
	suitableJDK := findJDK(curSys)
	if suitableJDK != "" {
	if suitableJDK == "" {
		return "", errors.New(T("err.java_required"))
	}

	destinationFolder := filepath.Join(userMaxDir, "jdk")
		if !dirExists(destinationFolder) {
	javaBin := bundledJavaPath(destinationFolder, curSys)
	if javaBin == "" {
		return "", errors.New(T("err.java_required"))
	}

	if fileExists(javaBin) {
		if err := ensureBundledJavaRuntimeExecutables(javaBin); err != nil {
			return "", err
		}
		return javaBin, nil
	}

	_ = os.RemoveAll(destinationFolder)
	if err := os.MkdirAll(destinationFolder, 0o755); err != nil {
		return "", err
	}

	destinationArchive := filepath.Join(userMaxDir, filepath.Base(suitableJDK))
	if err := cachedDownload(suitableJDK, destinationArchive); err != nil {
		return "", err
@@ -1174,25 +1189,27 @@ func ensureJava() (string, error) {
	if err := extractArchive(destinationArchive, destinationFolder); err != nil {
		return "", err
	}
		}

		var javaBin string
		switch curSys {
		case "linux/x86_64", "linux/amd64":
			javaBin = filepath.Join(destinationFolder, "jdk-22.0.2", "bin", "java")
		case "windows/x86_64", "windows/amd64":
			javaBin = filepath.Join(destinationFolder, "jdk-22.0.2", "bin", "java.exe")
		case "darwin/arm64":
			javaBin = filepath.Join(destinationFolder, "jdk-22.0.2.jdk", "Contents", "Home", "bin", "java")
	if !fileExists(javaBin) {
		return "", errors.New(T("err.java_required"))
	}
		if fileExists(javaBin) {
			if err := ensureExecutable(javaBin); err != nil {
	if err := ensureBundledJavaRuntimeExecutables(javaBin); err != nil {
		return "", err
	}
	return javaBin, nil
}

func bundledJavaPath(destinationFolder, curSys string) string {
	switch curSys {
	case "linux/x86_64", "linux/amd64", "linux/arm64":
		return filepath.Join(destinationFolder, "jdk-22.0.2", "bin", "java")
	case "windows/x86_64", "windows/amd64":
		return filepath.Join(destinationFolder, "jdk-22.0.2", "bin", "java.exe")
	case "darwin/arm64":
		return filepath.Join(destinationFolder, "jdk-22.0.2.jdk", "Contents", "Home", "bin", "java")
	default:
		return ""
	}
	return "", errors.New(T("err.java_required"))
}

func ensureExecutable(filePath string) error {
@@ -1211,6 +1228,23 @@ func ensureExecutable(filePath string) error {
	return os.Chmod(filePath, mode|0o111)
}

func ensureBundledJavaRuntimeExecutables(javaBin string) error {
	if err := ensureExecutable(javaBin); err != nil {
		return err
	}
	if currentOS() == "windows" {
		return nil
	}
	javaHome := filepath.Dir(filepath.Dir(javaBin))
	jspawnhelper := filepath.Join(javaHome, "lib", "jspawnhelper")
	if fileExists(jspawnhelper) {
		if err := ensureExecutable(jspawnhelper); err != nil {
			return err
		}
	}
	return nil
}

func syncBundles(rootDirectory string) error {
	ignoreFile := ".ignore"
	installedFile := ".installed"
@@ -2768,7 +2802,7 @@ func extractZip(src, destination string) error {
			return fmt.Errorf(T("err.invalid_zip_entry"), f.Name)
		}
		if f.FileInfo().IsDir() {
			if err := os.MkdirAll(cleanTarget, 0o755); err != nil {
			if err := os.MkdirAll(cleanTarget, effectivePerm(f.Mode(), 0o755)); err != nil {
				return err
			}
			continue
@@ -2780,7 +2814,7 @@ func extractZip(src, destination string) error {
		if err != nil {
			return err
		}
		out, err := os.Create(cleanTarget)
		out, err := os.OpenFile(cleanTarget, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, effectivePerm(f.Mode(), 0o644))
		if err != nil {
			in.Close()
			return err
@@ -2827,14 +2861,14 @@ func extractTarGz(src, destination string) error {
		}
		switch hdr.Typeflag {
		case tar.TypeDir:
			if err := os.MkdirAll(cleanTarget, 0o755); err != nil {
			if err := os.MkdirAll(cleanTarget, effectivePerm(hdr.FileInfo().Mode(), 0o755)); err != nil {
				return err
			}
		case tar.TypeReg:
		case tar.TypeReg, tar.TypeRegA:
			if err := os.MkdirAll(filepath.Dir(cleanTarget), 0o755); err != nil {
				return err
			}
			out, err := os.Create(cleanTarget)
			out, err := os.OpenFile(cleanTarget, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, effectivePerm(hdr.FileInfo().Mode(), 0o644))
			if err != nil {
				return err
			}
@@ -2843,7 +2877,37 @@ func extractTarGz(src, destination string) error {
				return err
			}
			out.Close()
		case tar.TypeSymlink:
			if err := os.MkdirAll(filepath.Dir(cleanTarget), 0o755); err != nil {
				return err
			}
			_ = os.Remove(cleanTarget)
			if err := os.Symlink(hdr.Linkname, cleanTarget); err != nil {
				return err
			}
		case tar.TypeLink:
			linkTarget := filepath.Join(destination, hdr.Linkname)
			cleanLinkTarget := filepath.Clean(linkTarget)
			cleanDest := filepath.Clean(destination)
			if !strings.HasPrefix(cleanLinkTarget, cleanDest+string(os.PathSeparator)) && cleanLinkTarget != cleanDest {
				return fmt.Errorf(T("err.invalid_tar_entry"), hdr.Linkname)
			}
			if err := os.MkdirAll(filepath.Dir(cleanTarget), 0o755); err != nil {
				return err
			}
			_ = os.Remove(cleanTarget)
			if err := os.Link(cleanLinkTarget, cleanTarget); err != nil {
				return err
			}
		}
	}
	return nil
}

func effectivePerm(mode os.FileMode, fallback os.FileMode) os.FileMode {
	perm := mode.Perm()
	if perm == 0 {
		return fallback
	}
	return perm
}
+56 −0
Original line number Diff line number Diff line
package main

import (
	"archive/tar"
	"compress/gzip"
	"net/url"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)
@@ -102,3 +105,56 @@ func TestProjectsJSONStorageLifecycle(t *testing.T) {
		t.Fatalf("expected 0 project rows after deletion, got %d", len(rows))
	}
}

func TestExtractTarGzPreservesExecutableBit(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Skip("executable permission bits are not relevant on windows")
	}

	archivePath := filepath.Join(t.TempDir(), "jdk.tar.gz")
	archiveFile, err := os.Create(archivePath)
	if err != nil {
		t.Fatal(err)
	}

	gz := gzip.NewWriter(archiveFile)
	tw := tar.NewWriter(gz)

	content := []byte("#!/bin/sh\necho ok\n")
	hdr := &tar.Header{
		Name:     "jdk-22.0.2/bin/java",
		Mode:     0o755,
		Size:     int64(len(content)),
		Typeflag: tar.TypeReg,
	}
	if err := tw.WriteHeader(hdr); err != nil {
		t.Fatal(err)
	}
	if _, err := tw.Write(content); err != nil {
		t.Fatal(err)
	}

	if err := tw.Close(); err != nil {
		t.Fatal(err)
	}
	if err := gz.Close(); err != nil {
		t.Fatal(err)
	}
	if err := archiveFile.Close(); err != nil {
		t.Fatal(err)
	}

	dest := t.TempDir()
	if err := extractTarGz(archivePath, dest); err != nil {
		t.Fatal(err)
	}

	extracted := filepath.Join(dest, "jdk-22.0.2", "bin", "java")
	info, err := os.Stat(extracted)
	if err != nil {
		t.Fatal(err)
	}
	if info.Mode()&0o111 == 0 {
		t.Fatalf("expected executable mode on %s, got %v", extracted, info.Mode())
	}
}