Files
streamed-tui/internal/dependencies.go
2025-11-23 01:53:57 -05:00

105 lines
2.5 KiB
Go

package internal
import (
"archive/tar"
"bytes"
"compress/gzip"
"crypto/sha256"
_ "embed"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"time"
)
//go:embed assets/node_modules.tar.gz
var embeddedNodeModules []byte
// ensureEmbeddedNodeModules extracts the bundled Node.js dependencies into a
// deterministic cache directory derived from the archive hash and returns the
// path that contains the resulting node_modules directory.
func ensureEmbeddedNodeModules() (string, error) {
if len(embeddedNodeModules) == 0 {
return "", errors.New("no embedded node modules archive available")
}
sum := sha256.Sum256(embeddedNodeModules)
hashPrefix := hex.EncodeToString(sum[:8])
cacheRoot, err := os.UserCacheDir()
if err != nil {
cacheRoot = os.TempDir()
}
baseDir := filepath.Join(cacheRoot, "streamed-tui", "node_modules", hashPrefix)
marker := filepath.Join(baseDir, ".complete")
if _, err := os.Stat(marker); err == nil {
return baseDir, nil
}
if err := os.RemoveAll(baseDir); err != nil {
return "", fmt.Errorf("failed to clear embedded node cache: %w", err)
}
if err := os.MkdirAll(baseDir, 0o755); err != nil {
return "", fmt.Errorf("failed to create embedded node cache: %w", err)
}
if err := untarGzip(bytes.NewReader(embeddedNodeModules), baseDir); err != nil {
return "", fmt.Errorf("failed to extract embedded node modules: %w", err)
}
if err := os.WriteFile(marker, []byte(time.Now().Format(time.RFC3339)), 0o644); err != nil {
return "", fmt.Errorf("failed to mark embedded node modules ready: %w", err)
}
return baseDir, nil
}
func untarGzip(r io.Reader, dest string) error {
gz, err := gzip.NewReader(r)
if err != nil {
return err
}
defer gz.Close()
tr := tar.NewReader(gz)
for {
hdr, err := tr.Next()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return err
}
target := filepath.Join(dest, hdr.Name)
switch hdr.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(target, os.FileMode(hdr.Mode)); err != nil {
return err
}
case tar.TypeReg:
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
return err
}
f, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(hdr.Mode))
if err != nil {
return err
}
if _, err := io.Copy(f, tr); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
default:
// Ignore unsupported entries to keep extraction simple.
}
}
return nil
}