~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
124 lines · 3.3 KB Raw History
package policytest

import (
	"archive/zip"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"sync"

	"github.com/johnandersen777/socialweb-computer-kcp/internal/policyengine/deno"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/versions"
)

const DenoEnv = "SOCIALWEB_POLICYTEST_DENO"

var (
	denoOnce sync.Once
	denoPath string
	denoFrom string
	denoErr  error
)

func DenoPath() (string, string, error) {
	denoOnce.Do(func() { denoPath, denoFrom, denoErr = locateDeno() })
	return denoPath, denoFrom, denoErr
}

func locateDeno() (string, string, error) {
	if path := strings.TrimSpace(os.Getenv(DenoEnv)); path != "" {
		if info, err := os.Stat(path); err != nil || info.IsDir() {
			return "", "", fmt.Errorf("%s=%s is not a deno binary", DenoEnv, path)
		}
		return path, DenoEnv, nil
	}
	cacheDir, err := os.UserCacheDir()
	if err != nil {
		cacheDir = filepath.Join(os.TempDir(), "policytest-cache")
	}
	cacheDir = filepath.Join(cacheDir, "policy_engine")
	if len(deno.EmbeddedZip()) > 0 {
		if path, err := deno.Resolve(cacheDir); err == nil {
			return path, "the embedded deno (build tag embed_deno)", nil
		}
	}
	if path, from, err := stagedDeno(cacheDir); err == nil {
		return path, from, nil
	}
	if path, err := exec.LookPath("deno"); err == nil {
		return path, "deno on PATH", nil
	}
	return "", "", errors.New("no deno for the policy engine: set " + DenoEnv + ", or run scripts/build-socialweb-computer.sh --dev once " +
		"so the pinned deno zip is staged in ~/.tmp/socialweb-computer-build/cache/deno-embed, or put deno " + deno.Version + " on PATH")
}

func stagedDeno(cacheDir string) (string, string, error) {
	platform := runtime.GOOS + "/" + runtime.GOARCH
	want, err := versions.FetchSHA256("deno", platform)
	if err != nil {
		return "", "", err
	}
	home, err := os.UserHomeDir()
	if err != nil {
		return "", "", err
	}
	zipPath := filepath.Join(home, ".tmp", "socialweb-computer-build", "cache", "deno-embed", want+".zip")
	dir := filepath.Join(cacheDir, "deno-embedded", deno.Version)
	path := filepath.Join(dir, "deno")
	from := "the build-staged pinned deno " + zipPath
	if info, err := os.Stat(path); err == nil && !info.IsDir() {
		return path, from, nil
	}
	data, err := os.ReadFile(zipPath)
	if err != nil {
		return "", "", err
	}
	sum := sha256.Sum256(data)
	if hex.EncodeToString(sum[:]) != want {
		return "", "", fmt.Errorf("%s does not match its pin", zipPath)
	}
	if err := extractDeno(zipPath, dir); err != nil {
		return "", "", err
	}
	return path, from, nil
}

func extractDeno(zipPath, dir string) error {
	reader, err := zip.OpenReader(zipPath)
	if err != nil {
		return err
	}
	defer reader.Close()
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return err
	}
	for _, file := range reader.File {
		if filepath.Base(file.Name) != "deno" {
			continue
		}
		in, err := file.Open()
		if err != nil {
			return err
		}
		out, err := os.CreateTemp(dir, "deno.*.tmp")
		if err != nil {
			in.Close()
			return err
		}
		_, copyErr := io.Copy(out, in)
		in.Close()
		if err := errors.Join(copyErr, out.Close(), os.Chmod(out.Name(), 0o755)); err != nil {
			os.Remove(out.Name())
			return err
		}
		return os.Rename(out.Name(), filepath.Join(dir, "deno"))
	}
	return fmt.Errorf("%s holds no deno binary", zipPath)
}